Android: Managing AsyncTask, progress dialog and screen orientation

For my app Moustachify Everything, I was getting quite a few 1-star ratings when users got a black screen for the results.

It took me a while to figure out why it was happening. Because I test on WiFi, the images upload quite quickly.

When testing with 3G, I accidentally rotated the screen and the progress dialog was cleared, leaving me with just a black screen and an ad.

When the AsyncTask completes, it updates the image (in the old Activity) but the image in the current Activity is still black.

To fix this, you'll need to keep track of your AsyncTask. It sounds longer than it actually is, but you'll learn some new tricks on the way.

The AsyncTask

If using an anonymous instance of AsyncTask, extract it out into it's own class or into an inner class of the activity. You'll need to be able to store a reference to it somewhere.

Important things to do are:

  • Keep a reference to the current activity
  • Keep an instance of the ProgressDialog

The Activity

Using onRetainNonConfigurationInstance(), we pass the new Activity the existing information about the AsyncTask.

This is how we update the "current activity" within the AsyncTask.

Important things to note are:

  • Keep an instance of your AsyncTask
  • Implement onRetainNonConfigurationInstance()
  • During onCreate(), check getLastNonConfigurationInstance()

A working example

public class SomeActivity extends Activity {
private ProgressAsyncTask m_progressTask;

// This is purely a data storage class for saving information between rotations
private class LastConfiguration {
Drawable drawable;
ProgressAsyncTask progressTask;

public LastConfiguration(Drawable drawable, ProgressAsyncTask progressTask) {
this.drawable = drawable;
this.progressTask = progressTask;
}
}

private class ProgressAsyncTask extends AsyncTask<String, Integer, Bitmap> {
private ProgressDialog progressDialog;
private Activity m_activity;

protected ProgressAsyncTask(Activity activity) {
setActivity(activity);
}

public void setActivity(Activity activity) {
m_activity = activity;

progressDialog = new ProgressDialog(m_activity);
progressDialog.setMessage("Growing your mo ...");
progressDialog.setCancelable(false);
progressDialog.setMax(100);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

progressDialog.show();
}

// Just some example code to update your progress dialog
protected void onProgressUpdate(Integer... values) {
progressDialog.setProgress((int) ((values[0] / (float) values[1]) * 100));
};

// Once we're done, make sure you reference the activity
@Override
protected void onPostExecute(Bitmap result) {
ImageView img = (ImageView) m_activity.findViewById(R.id.imgResult);
img.setImageBitmap(result);
progressDialog.hide();
}
}

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

// See if we've got any information from the existing Activity
LastConfiguration lastConfiguration = (LastConfiguration) this.getLastNonConfigurationInstance();

// Screen was rotated, re-apply data we already have
if (lastConfiguration != null) {
ImageView img = (ImageView) findViewById(R.id.imgResult);
img.setImageDrawable(lastConfiguration.drawable);
m_progressTask = lastConfiguration.progressTask;
// Update the task because it is currently pointing to a stale activity
m_progressTask.setActivity(this);
}
// New instance of SomeActivity, fetch image
else {
m_progressTask = new ProgressAsyncTask(this);
m_progressTask.execute("http://www.url.com/to/file.jpg");
}
}

// Remember the information when the screen is just about to be rotated.
// This information can be retrieved by using getLastNonConfigurationInstance()
public Object onRetainNonConfigurationInstance() {
ImageView iv = (ImageView) findViewById(R.id.imgResult);
return new LastConfiguration(iv.getDrawable(), m_progressTask);
}
}

Source

 
Copyright © Twig's Tech Tips
Theme by BloggerThemes & TopWPThemes Sponsored by iBlogtoBlog