Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WeakReference/AsyncTask pattern in android

I have a question regarding this simple frequently occurring situation in android .

We have a main activity , we invoke an AsyncTask alongwith the reference of the mainactivity , so that that the AsyncTask can update the views on the MainActivity.

I will break down the event into steps

  • MainActivity creates an AyncTask , passes its reference to it .
  • AysncTask , starts it's work , downloading ten files for example
  • The user changed the orientation of the device. This results in an orphan pointer in the AsyncTask
  • When the AsyncTask completes , and tries to access the activity to update the status , it crashes , because of the null pointer .

The solution for the above is to keep a WeakReference in the AsyncTask as recommended by the book "Pro Android 4"

WeakReference<Activity> weakActivity;

in method onPostExecute

Activity activity = weakActivity.get();
if (activity != null) {
   // do your stuff with activity here
}

How does this resolve the situation ?

My question it , if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.

What would happen to the previous AsyncTask that was initially invoked ?

Thank you , and I apologize for the length of the question .

like image 327
Muhammad Ahmed AbuTalib Avatar asked Aug 18 '13 08:08

Muhammad Ahmed AbuTalib


People also ask

What is AsyncTask in Android with example?

Android AsyncTask is an abstract class provided by Android which gives us the liberty to perform heavy tasks in the background and keep the UI thread light thus making the application more responsive. Android application runs on a single thread when launched.

What is the problem with AsyncTask in Android?

In summary, the three most common issues with AsyncTask are: Memory leaks. Cancellation of background work. Computational cost.

Is AsyncTask deprecated in Android?

AsyncTask is used to perform time talking operations in android, but it's marked as deprecated from android 11.

What is a WeakReference Android?

WeakReference: a weak reference is a reference not strong enough to keep the object in memory. If we try to determine if the object is strongly referenced and it happened to be through WeakReferences, the object will be garbage-collected.


3 Answers

How does this resolve the situation ?

The WeakReference allows the Activity to be garbage collected, so you don't have a memory leak.

A null reference means that the AsyncTask cannot blindly try to update a user-interface that is no longer attached, which would throw exceptions (e.g. view not attached to window manager). Of course you have to check for null to avoid NPE.

if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.

Depends on your implementation, but probably yes - if you don't deliberately do something to make a repeat download unnecessary, such as caching the results somewhere.

What would happen to the previous AsyncTask that was initially invoked ?

In earlier versions of Android it would run to completion, downloading all of the files only to throw them away (or perhaps cache them, depending on your implementation).

In newer Android's I am suspicious that AsyncTask's are being killed along with the Activity that started them, but my basis for suspicion is only that the memory-leak demo's for RoboSpice (see below) do not actually leak on my JellyBean devices.

If I may offer some advice: AsyncTask is not suitable for performing potentially long running tasks such as networking.

IntentService is a better (and still relatively simple) approach, if a single worker thread is acceptable to you. Use a (local) Service if you want control over the thread-pool - and be careful not to do work on the main thread!

RoboSpice seems good if you are looking for a way to reliably perform networking in the background (disclaimer: I have not tried it; I am not affiliated). There is a RoboSpice Motivations demo app in the play store which explains why you should use it by demo-ing all the things that can go wrong with AsyncTask - including the WeakReference workaround.

See also this thread: Is AsyncTask really conceptually flawed or am I just missing something?

Update:

I created a github project with an example of downloading using IntentService for another SO question (How to fix android.os.NetworkOnMainThreadException?), but it is also relevant here, I think. It has the added advantage that, by returning the result via onActivityResult, a download that is in flight when you rotate the device will deliver to the restarted Activity.

like image 93
Stevie Avatar answered Oct 08 '22 05:10

Stevie


The WeakReference class basically just prevents the JRE to increase the reference counter for the given instance.

I won't go into Java's memory management and answer your question directly: The WeakReference resolves the situation by providing the AsyncTask a way to learn if its parent activity is still valid.

The orientation change itself will not automatically restart the AsyncTask. You have to code the desired behavior with the known mechanisms (onCreate/onDestroy, onSave/RestoreInstanceState).

Concerning the original AsyncTask, I'm not 100 % sure which of these options will happen:

  • Either Java stops the thread and disposes the AsyncTask, because the only object holding a reference to it (the original Activity) is destroyed
  • Or some internal Java object maintains a reference to the AsyncTask object, blocking its garbage collection, effectively leaving the AsyncTask to finish in the background

Either way, it would be good practice to abort/pause and restart/resume the AsyncTask manually (or handing it over to the new Activity), or use a Service instead.

like image 23
domsom Avatar answered Oct 08 '22 05:10

domsom


How does this resolve the situation ?

It doesn't.

The referent of a WeakReference is set to null when the garbage collector determines that the referent is weakly reachable. This does not happen when an activity is paused, and does not necessarily happen immediately when the activity is destroyed and the framework discards all references to it. If the GC has not run, it is entirely possible for the AsyncTask to complete while its WeakReference still contains a reference to a dead activity.

Not only that, but this approach does nothing to prevent the AsyncTask from uselessly consuming CPU.

A better approach is to have the Activity maintain a strong reference to the AsyncTask and cancel(...) it in the appropriate teardown lifecycle method. The AsyncTask should monitor isCancelled() and stop working if it is no longer needed.

If you want an AsyncTask to survive across configuration changes (but not other forms of activity destruction) you can host it in a retained fragment.

like image 39
Kevin Krumwiede Avatar answered Oct 08 '22 06:10

Kevin Krumwiede