1

In my app, I have a class that inherits from AsyncTask and which downloads huge amounts of data from the server. I am using a ProgressBar to indicate the progress of the download.

When the user hits the HOME key, the Activity to which this AsyncTask is attached, is destroyed but, download goes on.

How can I reattach this AsyncTask and show the progress to user? I tried using onRetainNonConfigurationInstance but Android 4.0 doesn't seem to invoke this method. My application does not use Fragments API.

Sagar Hatekar
  • 8,700
  • 14
  • 56
  • 72
dimika
  • 131
  • 2
  • 9

3 Answers3

4

What I did in this situation was as follows:

  1. I created an IntentService to handle communication with the server. This has some of the benefits of AsyncTask (e.g., worker thread), but also some benefits of a Service (available any time, from anywhere).
  2. The IntentService can be invoked either by a user action in my main Activity, or via an inexact repeating alarm.
  3. The data is stored in an SQLite database, fronted by a ContentProvider. I dodge the issue of when/how to create my database and tables by using an SQLiteOpenHelper and calling getWritableDatabase() from the safety of my background IntentService.
  4. When the task is done, it posts a Notification if my main Activity is not active.

One nice thing about this arrangement is, no progress bar is necessary. In fact, no UI is necessary. The user keeps using the application while the service is running, and the UI automatically refreshes itself as new data comes into the ContentProvider. Another nice aspect of it is it's less code to write than an AsyncTask. It automatically picks up where it last left off by reading the server-side metadata of the last entry from the database and asking the user to start after that point. Since it's not tied to any Activity instance, it doesn't care about onPostExecute() or configuration changes or any of that. And you don't have to worry about single-shot execution like AsyncTask.

Sparky
  • 8,437
  • 1
  • 29
  • 41
  • 1
    +1 for suggesting `IntentService`. One thing I'd add, however, is if the OP really does want progress indication, it is actually possible to post multiple ongoing `Notifications` which either contain a `ProgressBar` or perhaps just textual progress information. – Squonk Aug 04 '12 at 20:46
  • Thank you for that addition, Squonk. In my app, the presence of new rows in my ListView implicitly demonstrates progress. But this model doesn't hold up so well for downloading one big file. – Sparky Aug 04 '12 at 20:49
  • OK, It`s sings good. One more questions for my understanding: I use some AsyncTask for prepare SQL Temp Tables for using in my Activities and I have no ContentProvider for this. What should I use in this case? – dimika Aug 04 '12 at 20:56
  • Should I use service from simple HTTP-requests to server instead of AsyncTask too? – dimika Aug 04 '12 at 20:58
  • I used the IntentService mainly because I'm lazy and it looked like less code to write than AsyncTask. It's a challenge not being able to access any member variables in the Activity, but nice not to be dependent on it. For creating SQL tables, you could go either way: you're unlikely to lose your Activity in the middle. Messaging status back to the calling Activity might be an issue. I don't use direct two-way communication, you notice. – Sparky Aug 04 '12 at 21:06
1

If there is a need to download huge amount of data in background I would use service rather then AsyncTask. There is a good presentation from Google IO about using services.

Quote from AsyncTask documentation:

If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.

and

The task can be executed only once (an exception will be thrown if a second execution is attempted.)

As I understand, you cannot proceed with your last AsyncTask. Still, you can load your data partially and save amount of data read and then start new AsyncTask which will start from last saved point. From my point of view this is not the best idea to pause loading when activity goes to background and it is better to use service to finish what was started.

marwinXXII
  • 1,456
  • 14
  • 21
  • +1 Agreed - for longer running tasks, use a Service. Out of curiousity, is there a way to get notified when an AsyncTask finishes and then invoke a new Activity if the old Activity instance is destroyed? – Sagar Hatekar Aug 04 '12 at 20:14
  • 1
    @SagarHatekar use the onPostExecute() method to create an intent that launches a new activity if an older one isn't running yet. – A Random Android Dev Aug 04 '12 at 20:16
  • Its not longer task - max time is 10 second and I cannot to save part of data and then start from saved point. I need to show progress dialog again if task is not complete and attach Activity to invoke OnTaskComplete callback. – dimika Aug 04 '12 at 20:21
  • I think that you cannot be sure that onPostExecute will be invoked when Activity was already destroyed. UI thread might be already stopped, so your message with posting result will not be received. – marwinXXII Aug 04 '12 at 20:22
  • If it is not a long task, then you shouldn't worry so much that your activity will be destroyed, it will be only stopped. – marwinXXII Aug 04 '12 at 20:22
  • – marwinXXII - On Android 4.0 Activity destroys immediately after pressing Home key. – dimika Aug 04 '12 at 20:27
  • sounds strange, but have you tried this solution? http://stackoverflow.com/questions/6772988/ondestroy-gets-called-each-time-the-screen-goes-on – marwinXXII Aug 04 '12 at 20:35
  • When screen orientation changed fired the next: onSaveInstanceState() onRetainNonConfigurationInstance() onDestroy(). When Home key is pressed only the following: onSaveInstanceState() onDestroy(). Android 4.0 Galaxy Tab 2. – dimika Aug 04 '12 at 20:41
0

Have you considered using a service to attach your AsyncTask to? Seeing as a permanently running service would probably be the best solution for your task at hand. All you'd have to do then will be to check if the service is running and if your download is running (easily done using static boolean variables) then you just create a progress dialog using some state saving variable in your service (maybe a percentage of the total file size downloaded etc.) in the onCreate method of your main activity.

A Random Android Dev
  • 2,691
  • 1
  • 18
  • 17