Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android AsyncTask context behavior

I've been working with AsyncTasks in Android and I am dealing with an issue.

Take a simple example, an Activity with one AsyncTask. The task on the background does not do anything spectacular, it just sleeps for 8 seconds.

At the end of the AsyncTask in the onPostExecute() method I am just setting a button visibility status to View.VISIBLE, only to verify my results.

Now, this works great until the user decides to change his phones orientation while the AsyncTask is working (within the 8 second sleep window).

I understand the Android activity life cycle and I know the activity gets destroyed and recreated.

This is where the problem comes in. The AsyncTask is referring to a button and apparently holds a reference to the context that started the AsyncTask in the first place.

I would expect, that this old context (since the user caused an orientation change) to either become null and the AsyncTask to throw an NPE for the reference to the button it is trying to make visible.

Instead, no NPE is thrown, the AsyncTask thinks that the button reference is not null, sets it to visible. The result? Nothing is happening on the screen!

Update: I have tackled this by keeping a WeakReference to the activity and switching when a configuration change happens. This is cumbersome.

Here's the code:

public class Main extends Activity {      private Button mButton = null;     private Button mTestButton = null;      @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.main);          mButton = (Button) findViewById(R.id.btnStart);         mButton.setOnClickListener(new OnClickListener () {             @Override             public void onClick(View v) {                 new taskDoSomething().execute(0l);             }         });         mTestButton = (Button) findViewById(R.id.btnTest);        }      private class TaskDoSomething extends AsyncTask<Long, Integer, Integer>      {         @Override         protected Integer doInBackground(Long... params) {             Log.i("LOGGER", "Starting...");             try {                 Thread.sleep(8000);             } catch (InterruptedException e) {                 e.printStackTrace();             }             return 0;         }          @Override         protected void onPostExecute(Integer result) {             Log.i("LOGGER", "...Done");             mTestButton.setVisibility(View.VISIBLE);         }     } } 

Try executing it and while the AsyncTask is working change your phones orientation.

like image 659
dnkoutso Avatar asked Jan 23 '10 20:01

dnkoutso


People also ask

Why is Android AsyncTask deprecated?

This class was deprecated in API level 30. AsyncTask was intended to enable proper and easy use of the UI thread. However, the most common use case was for integrating into UI, and that would cause Context leaks, missed callbacks, or crashes on configuration changes.

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 are the problems in AsyncTask?

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

What can I use instead of AsyncTask?

Alternative 1: Using Executor and Handler The executor will help in performing any task in the background and the handler will help to make UI changes.


2 Answers

AsyncTask is not designed to be reused once an Activity has been torn down and restarted. The internal Handler object becomes stale, just like you stated. In the Shelves example by Romain Guy, he simple cancels any currently running AsyncTask's and then restarts new ones post-orientation change.

It is possible to hand off your Thread to the new Activity, but it adds a lot of plumbing. There is no generally agreed on way to do this, but you can read about my method here : http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html

like image 113
jasonhudgins Avatar answered Oct 02 '22 21:10

jasonhudgins


If you only need a context and won't use it for ui stuff you can simply pass the ApplicationContext to your AsyncTask.You often need the context for system resources, for example.

Don't try to update the UI from an AsyncTask and try to avoid handling configuration changes yourself as it can get messy. In order to update the UI you could register a Broadcast receiver and send a Broadcast.

You should also have the AsyncTask as a separate public class from the activity as mentioned above, it makes testing a lot easier. Unfortunately Android programming often reinforces bad practices and the official examples are not helping.

like image 30
Ralph Mueller Avatar answered Oct 02 '22 19:10

Ralph Mueller