WeakReference/AsyncTask pattern in android

Solution 1:

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.

Solution 2:

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.

Solution 3:

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.