package com.alexvasilkov.android.commons.tasks;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import java.lang.ref.WeakReference;
/**
* Helper class that will call regular {@link android.os.AsyncTask AsyncTask} and store caller (parent object) into
* {@link java.lang.ref.WeakReference WeakReference}. All derived inner classes should be marked as static in most cases
* to not leak any {@link android.app.Activity Activities} or {@link android.app.Fragment Fragments}
*/
public abstract class BackgroundTask<P> {
private final WeakReference<P> mParentRef;
private boolean mIsSkipCallbacksIfParentIsNull;
private ATask mTask;
/**
* @param parent Parent object (i.e. {@link android.app.Activity Activity} or
* {@link android.app.Fragment Fragment}) to be passed to task's lyfecycle
* methods ({@link #onTaskStarted(Object) onTaskStarted},
* {@link #onTaskSuccess(Object) onTaskSuccess},
* {@link #onTaskFail(Object, Exception) onTaskFail}
* or {@link #onTaskEnded(Object) onTaskEnded})
*/
public BackgroundTask(P parent) {
mParentRef = new WeakReference<P>(parent);
mIsSkipCallbacksIfParentIsNull = (parent != null); // only skipping callbacks if origin parent is null
}
protected void onTaskStarted(P parent) {
}
protected abstract void doTask() throws Exception;
/**
* @param skip Parent object is saved into {@link java.lang.ref.WeakReference
* WeakReference} so it can be erased and we won't
* be able to pass it to task's lyfecycle methods. Default behavior is to skip
* calling these methods if parent object is <code>null</code> to prevent NPE,
* but you can change this if you must be sure that all callback methods are
* always called.
*/
protected void setSkipCallbacksIfParentIsNull(boolean skip) {
mIsSkipCallbacksIfParentIsNull = skip;
}
/**
* Calls {@link android.os.AsyncTask#publishProgress(Object[]) AsyncTask.publishProgress()} method.
* Corresponding callback method is {@link #onTaskProgress(Object) onTaskProgress}.<br/>
* Should only be called from {@link #doTask() doTask} method.
*/
protected void publishTaskProgress() {
if (mTask != null) mTask.publishTaskProgress();
}
protected void onTaskProgress(P parent) {
}
protected void onTaskSuccess(P parent) {
}
protected void onTaskFail(P parent, Exception e) {
}
protected void onTaskEnded(P parent) {
}
private P getParent() {
return mParentRef.get();
}
public void exec() {
mTask = new ATask();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
} else {
mTask.execute((Void) null);
}
}
public void cancel(boolean mayInterruptIfRunning) {
if (mTask != null) mTask.cancel(mayInterruptIfRunning);
}
private class ATask extends AsyncTask<Void, Void, Void> {
private Exception mException;
@Override
protected final void onPreExecute() {
super.onPreExecute();
P parent = getParent();
if (parent == null && mIsSkipCallbacksIfParentIsNull) {
cancel(false);
} else {
onTaskStarted(parent);
}
}
@Override
protected final Void doInBackground(Void... urls) {
try {
doTask();
} catch (Exception e) {
mException = e;
Log.e("BackgroundTask", "Exception", e);
}
return null;
}
private void publishTaskProgress() {
publishProgress();
}
@Override
protected void onProgressUpdate(Void... values) {
P parent = getParent();
if (parent == null && mIsSkipCallbacksIfParentIsNull) {
return;
}
onTaskProgress(parent);
}
@Override
protected final void onPostExecute(Void result) {
P parent = getParent();
if (parent == null && mIsSkipCallbacksIfParentIsNull) {
return;
}
if (mException == null) {
onTaskSuccess(parent);
} else {
onTaskFail(parent, mException);
}
onTaskEnded(parent);
}
/**
* Always make call to super.onCancelled(), then overriding this method!
*/
@Override
protected void onCancelled() {
super.onCancelled();
P parent = getParent();
if (parent != null || !mIsSkipCallbacksIfParentIsNull) {
onTaskEnded(parent);
}
}
}
}