Wednesday 8 October 2014

AsyncTask and context leaking

AsyncTask is mostly used as inner class of Activity or Fragment. For example:

public class SampleActivity extends Activity
{
    private static SampleActivity instance;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);

        new SampleTask().execute();
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        instance = null;
    }

    @Override
    protected void onResume()
    {
        super.onResume();
        instance = this;
    }

    private class SampleTask extends AsyncTask<Void, Void, String>
    {
        @Override
        protected String doInBackground(Void... params)
        {
            // function calls
            return "dummyResult";
        }

        @Override
        protected void onPostExecute(String result)
        {
            super.onPostExecute(result);

            if(SampleActivity.instance != null)
            {
                onResult(result);
            }
        }

    }

    private void onResult(String result)
    {
        // Update UI
    }

}

The problem with the above approach is that as long as SampleTask is running, SampleActivity will not be garbage collected even after being stopped. SampleTask needs an instance of activity because it is declared inside the SampleActivity.

The right way to use AsyncTask inside Activity is to define it as static. Static members don't need instance of the class so the activity will be garbage collected (after it is stopped) even though if task is running. To update the UI from task we need instance of the activity, and for that we can keep weakRefrence of activity inside the Asynctask. WeakRefrence allow the activity to be garbage collected once it is stopped. The above code can be re-written like this:

public class SampleActivity extends Activity
{
    private static SampleTask task;
   
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);

        task = new SampleTask(this);
        task.execute();
    }
    @Override
    protected void onPause()
    {
        super.onPause();
        if(task != null)
        {
            task.cancel(true);
            task = null;
        }
    }
    private static class SampleTask extends AsyncTask<Void, Void, String>
    {
        WeakReference<SampleActivity> activityRef;

        public SampleTask(SampleActivity activity)
        {
            activityRef = new WeakReference<SampleActivity>(activity);
        }

        @Override
        protected String doInBackground(Void... params)
        {
            // function calls
            return "dummyResult";
        }

        @Override
        protected void onPostExecute(String result)
        {
            super.onPostExecute(result);

            SampleActivity activity = activityRef.get();

            if(activity != null)
            {
                activity.onResult(result);
            }
        }

    }

    private void onResult(String result)
    {
        // update UI
    }

}