4

In Android development, I've learnt that AsyncTask defined as a non-static nested class is not ideal because it may cause memory leaks when the Activity that started the task, dies before the task has finished processing.

So the solution to use Loaders, whose lifecycle is independent to that of the activity.

However, what about in a situation like this where they've defined an anonymous AsyncTaskLoader. It looks to me that this Loader has a reference to its outer activity.

(1) Does this not cause a memory leak, where the starting activity is unable to be garbage collected?

Furthermore, the Loader's onStartLoading() method holds a reference to the outer activity's member mLoadingIndicator.

(2) If onCreateLoader is only called the first time the application launches, will this loader forever latch on to that first activity's mLoadingIndicator, ignoring the view from the new activity? (For example after configuration change)

Adrian Muljadi
  • 168
  • 1
  • 15

1 Answers1

1

However, what about in a situation like this where they've defined an anonymous AsyncTaskLoader. It looks to me that this Loader has a reference to its outer activity.

Yes, it has.

(1) Does this not cause a memory leak, where the starting activity is unable to be garbage collected?

Yes, it does. If this Loader runs indefinitely and longer than the encompassing Activity, it may prevent the garbage collection of the context.

(2) If onCreateLoader is only called the first time the application launches, will this loader will forever latch on to that first activity's mLoadingIndicator, ignoring the view from the new activity? (For example after configuration change)

onCreateLoader doesn't latch on to the view mLoadingIndicator is referencing, but it only calls one of its methods. What really matters is the object mLoadingIndicator refers to at the time onCreateLoader is invoked.

Actually the loader latches on to the outer activity. If a configuration change created a new loading indicator view and only then onCreateLoader is invoked, the method will use the new view.

Example

An AsyncTaskLoader can refer to an Activity without causing memory leak by wrapping it in a WeakReference.

public class MyAsyncTaskLoader extends AsyncTaskLoader<String> {

    private final WeakReference<Activity> mActivity;

    public MyAsyncTaskLoader(Activity activity) {
        super(activity);
        mActivity = new WeakReference<>(activity);
    }

    public doYourThing() {
        Activity activity = mActivity.get();

        // if activity is destroyed and garbage collected,
        // it will be null
        if (activity != null) {
            activity.getYourView().setWhatever();
        }
    }
}
nandsito
  • 3,782
  • 2
  • 19
  • 26
  • What happens in this scenario. Activity A calls initLoader(), hence Activty A's onCreateLoader() will be called and the loader is initialised. At this point in time, the loader has reference to activity A's `mLoadingIndicator` correct? Now a configuration change happens, and Activity A is destroyed, Activity B is created. The Loader is already initialised, so Activity B's `onCreateLoader` will never be called. Which Activity's `mLoadingIndicator` view will the loader refer to now? – Adrian Muljadi Apr 17 '17 at 16:13
  • @AdrianMuljadi the loader doesn't refer to a view but to the entire activity, which in turn refers to that view. If a config. change destroys the activity the loader is attached to, the loader will still refer to the same (destroyed) activity, and `mLoadingIndicator` will refer to the view of the destroyed activity. The new activity is a new object instance with its own loader – nandsito Apr 17 '17 at 16:28
  • @nandshito Got it, So what is best practice? Is it better to subclass AsnycTaskLoader in its own file? How would I have access to my activity's view then? (For example, to update a ProgressBar) – Adrian Muljadi Apr 17 '17 at 16:34
  • @AdrianMuljadi i don't say best practice, but yes, in this case you can prevent memory leak by subclassing asynctaskloader in its own file – nandsito Apr 17 '17 at 16:41
  • 1
    @AdrianMuljadi i added an example – nandsito Apr 17 '17 at 16:52
  • @nandshito Thanks for the `WeakReferencce` example, I learnt a lot. just to clarify your statement "_The new activity is a new object instance with its own loader_". Isn't the whole point of loaders such that it is independent of the activity life cycle. Hence from my example earlier, Activity A and B will both refer to the **same** loader given by the `LoaderManager` – Adrian Muljadi Apr 17 '17 at 17:57
  • @AdrianMuljadi my statement applies to the github sample you linked, which causes memory leak. Yes, the point is the loader be independent of any activity lifecycle – nandsito Apr 17 '17 at 18:17