Skip to content

Instantly share code, notes, and snippets.

@brnhffmnn
Created December 11, 2012 08:40
Show Gist options
  • Save brnhffmnn/4257045 to your computer and use it in GitHub Desktop.
Save brnhffmnn/4257045 to your computer and use it in GitHub Desktop.
An AsyncTask which is able to continue the work and update progress state even during configuration changes
package de.slowpoke;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
/**
* An {@link AsyncTask} which is able to continue the work and update progress state even during configuration changes
* (e.g. orientation change)<br>
* <br>
* <em>Adapted from <a href="https://github.com/commonsguy/cw-android/blob/master/Rotation/RotationAsync/src/com/commonsware/android/rotation/async/RotationAsync.java">RotationAsync.java</a></em>
*
* @author github.com/panzerfahrer
* @version 1.2
*
*/
public abstract class RotationAwareAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private boolean isFinished = false;
private Result savedResult;
private IRotationAwareTaskHolder<Progress> holder;
private Bundle lastErrorData;
/**
* Create a new task.
*
* @param holder
* most likely an {@link Activity}
*/
public RotationAwareAsyncTask(IRotationAwareTaskHolder<Progress> holder) {
this.holder = holder;
}
/**
* If you override this you must call through to the super implementation
*
* @see android.os.AsyncTask#onPreExecute()
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
this.holder.onStartRotationAwareTask();
}
@Override
final protected void onProgressUpdate(Progress... values) {
if (holder != null) {
holder.onUpdateRotationAwareTaskProgress(onRotationAwareProgressUpdate(values));
}
};
/**
* Called by {@link AsyncTask#onProgressUpdate(Object...)} as long as the holder is available.<br>
* <br>
* <em>Override if you need to process the progress value</em>
*
* @param values
* progress update initially from {@link AsyncTask#publishProgress(Progress)}
*/
protected Progress[] onRotationAwareProgressUpdate(Progress... values) {
return values;
}
@Override
final protected void onPostExecute(Result result) {
if (holder != null) {
if (!isCancelled()) {
onRotationAwarePostExecute(result);
}
holder.onFinishRotationAwareTask();
} else {
this.isFinished = true;
this.savedResult = result;
}
};
/**
* Called by {@link AsyncTask#onPostExecute(Object)} as long as the holder is available. If the holder is not
* available the result will be cached and this will be invoked right after
* {@link #attachAsyncTaskHolder(IRotationAwareTaskHolder)} is called
*
* @param values
*/
abstract protected void onRotationAwarePostExecute(Result values);
/**
* @return the associated holder or <code>null</code> if a configuration change is just happening
*/
protected IRotationAwareTaskHolder<Progress> getHolder() {
return this.holder;
}
/**
* Call if the task shall be canceled.
*/
abstract public void onCancel();
/**
* Notify this task in {@link Activity#onRetainNonConfigurationInstance()} or
* {@link FragmentActivity#onRetainCustomNonConfigurationInstance()} that a configuration change is happening and
* return this instance. <br>
* For example:
*
* <pre>
* public Object onRetainNonConfigurationInstance() {
* task.detachHolder();
* return task;
* }
* </pre>
*
* The implementation will make sure to release the current holder and to not update progress unless the holder is
* set.
*/
public void detachAsyncTaskHolder() {
this.holder = null;
}
/**
* Use to re-attach the holder after configuration change has happened. Get the task instance (which you previously
* returned in {@link Activity#onRetainNonConfigurationInstance()}) from
* {@link Activity#getLastNonConfigurationInstance()} or
* {@link FragmentActivity#getLastCustomNonConfigurationInstance()} in {@link Activity#onCreate(Bundle)} For
* example:
*
* <pre>
* protected void onCreate(Bundle savedInstanceState) {
* task = (RotationAwareAsyncTask) getLastNonConfigurationInstance();
*
* if (task == null) {
* task = new RotationAwareAsyncTask(this);
* } else {
* task.attachHolder(this);
* }
* }
* </pre>
*
* @param holder
*/
public void attachAsyncTaskHolder(IRotationAwareTaskHolder<Progress> holder) {
this.holder = holder;
if (this.isFinished) {
onRotationAwarePostExecute(this.savedResult);
holder.onFinishRotationAwareTask();
}
}
/**
* Interface for the {@link RotationAwareAsyncTask} to publish progress updates.
*
* @author brho
* @version 1.1
*/
public static interface IRotationAwareTaskHolder<Progress> {
public static final String ROTATION_AWARE_TASK_ERROR_TITLE = "title";
public static final String ROTATION_AWARE_TASK_ERROR_MESSAGE = "message";
/**
* Invoked by the {@link RotationAwareAsyncTask} to publish progress updates
*
* @param progress
* some type of progress status (most likely a <code>String</code> or <code>int</code>) which can be
* used to update a {@link ProgressDialog} or similar
*/
public void onUpdateRotationAwareTaskProgress(Progress... progress);
/**
* Invoked by the {@link RotationAwareAsyncTask} to notify that the work has been started
*/
public void onStartRotationAwareTask();
/**
* Invoked by the {@link RotationAwareAsyncTask} to notify that the work has been finished
*/
public void onFinishRotationAwareTask();
/**
* Invoked when an error occurred, giving the chance to display an error dialog. Mind that this method is not
* called from the UI thread, so you need to take care which code of yours needs to run on the UI thread.
*
* @param data
* a {@link Bundle} of data to be handed through. You might want to use one of the predefined keys.
*/
public void onShowRotationAwareTaskError(Bundle data);
/**
* @return the {@link Context} of the implementing instance (try not to return the {@link Activity} itself)
*/
public Context getContext();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment