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 originalActivity
) is destroyed - Or some internal Java object maintains a reference to the
AsyncTask
object, blocking its garbage collection, effectively leaving theAsyncTask
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.