views:

264

answers:

2

For instance, we are in SomeActivity and the activity has a button that invokes moving files from one dir to another (let's call it job).

On BlackBerry I would:

  1. push a non-cancellable popup (Dialog screen) saying "Please wait..."
  2. start a thread that fulfills the job
  3. on thread completion close the popup

This approach 99.99% can guarantee that we stay on the same screen after the task is over, user just sees the popup and waits for job completion. Device rotation or whatever does not break the desired workflow.

On Android things change. I know there's the AsyncTask that is probably provided to solve my case. There's even a good example of how it should be used. But since there's no guarantee of how long an Activity instance will live the AsyncTask should be cancelled on onSaveInstanceState (and restarted on onRestoreInstanceState). This means using AsyncTask there's no guarantee we are able to fully fulfill the job once started. In some cases as sending an http post request for creating a user I would not want to get in "user already exists for this login" trouble on reruning the AsyncTask. This is possible since the AsyncTask can be interrupted while the request is already sent (and the server actually is doing its job - creating a new user), but the AsyncTask is canceled before we got the response.

Is there any solution on Android to get the BB-like behaviour stated above?

+2  A: 

But since there's no guarantee of how long an Activity instance will live the AsyncTask should be cancelled on onSaveInstanceState (and restarted on onRestoreInstanceState).

Or have it be managed by a Service.

CommonsWare
I should study this component because I've been already adviced to use it twice.
Arhimed
Hi, CommonsWare.Is there a detailed explanation of how the long running jobs should be architectured/coded on Android in any of your books?
Arhimed
That depends a bit on what you define as "long running jobs". I cover services with AsyncTask in one book (http://commonsware.com/Android) -- see http://github.com/commonsguy/cw-android/tree/master/Service/WeatherPlus/ for the project in question. I cover the use of AlarmManager to avoid everlasting services in another book (http://commonsware.com/AdvAndroid) -- see http://github.com/commonsguy/cw-advandroid/tree/master/SystemServices/Alarm/ for that project.
CommonsWare
Saw your responce only now (I thought there would be some sort of email notification on new comment, but there wasn't).
Arhimed
I'm looking for a solution for proper long running job thread handling under progress popup assuming user invoked it from Activity. The problem is how to "syncronize" a job thread with Activity's life-cycle. That was my original post about.
Arhimed
So, your first one (service with AsyncTask in WeatherPlus) is of interest. Cool. As I got it the service will do its AsyncTask to the very end (the AsyncTask's doInBackground() will not be interrupted unless the whole app process is killed by OS). Thus device rotations will not cause the AsyncTask interruption. After the AsyncTask has finished - an Intent broadcast is being sent. Activity can handle the broadcast only being active (between onResume() and onPause()).
Arhimed
Sorry for probably silly questions:What will happen if at the time the broadcast is being sent the Activity has already been killed by OS (because of RAM shortage). Will the broadcast "wake up" the Activity to handle the broadcast?
Arhimed
No, the broadcast will simply be ignored.
CommonsWare
Thanks for the response, CommonsWare.I'm looking for the best class to be a dispatcher for AsyncTasks invoked from my Activities. It could be one of these:1. subclass of Application;2. subclass of Service;3. my own static stuff (a class an instance of which can not be created).As for me it's simlier to implement the 3rd choice. But the question is will it be more death-resistant than Service or Application? Also it's interesting what will live longer - Application or Service?
Arhimed
+1  A: 

If your Activity wants to stay on the screen, you can simply start a Thread like this:

final File fromFile = ...;
final File toFile = ...;

new Thread() {
    @Override
    public void run() {
      // do something with fromFile, toFile
    }
}.start();

That way the GUI-Thread is ready to do other thinks like displaying a

  android.app.ProgressDialog

Also, consider making the Dialog uncancellable with

  ProgressDialog.setCancelable(false);

That way the user can only leave via the HOME-Key, which you get notified of when

  Activity.onPause()

is called. Futhermore you might want to look into Wakelocks, to stop the Screen from turning black and your application pushed in the background where it might be killed. You'd do this in the Thread:

  PowerManager pm = (PowerManager) ivContext.getSystemService(Context.POWER_SERVICE);
  Wakelock wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "MyApp");
  wakeLock.acquire();

  // ... copy stuff ...

  wakeLock.release();

Of course you'd have to release the wakeLock, too, when the user leaves via the HOME-Key.

Finally if you want to call GUI-Elements from your background-thread, this will only work if the Thread is part of the GUI-Event-Loop, like the normal Thread is you are running in, when getting called with on...-Methods. To achieve this your background-thread will have to callback to the GUI-Thread via a Handler. Like this:

  private Handler mHandler = new Handler() { 
      @Override
      public void handleMessage(Message msg) {
          Log.v(TAG, "Got Message "+msg.what); // prints: Got Message 77
      // ... do GUI actions ...
      }    
  };

  // ... in Thread ...

  int lvInfo = 77;
  mHandler.sendEmptyMessage(lvInfo);

You can even put objects in the message like so:

  Message txtMsg = Message.obtain();
  textMsg.obj = "Hello World";
  mHandler.sendMessage(lvTextMsg);
rflexor
rflexor, thanks for your answer. Specifically for a Wakelocks point.
Arhimed