我的目标是有一个 AsyncTask
- 可以执行多次(当然一次一个任务)
- 可以取消其当前任务
- 可以被任何活动使用
- 可以执行许多不同的任务
- 屏幕旋转(或电话等)没有任何问题
为了实现这一点,我创建了如下所示的类。但是我对线程的经验(和理解)非常有限。而且由于我不知道调试多个线程的任何方法,因此(对我而言)无法知道这是否可行。所以我真正要问的是:这段代码可以吗?
而且由于目前没有使用它的代码,这里有一个使用它的示例:
Data2Get d2g = new Data2Get(this, Data2Get.OpCountNumbers);
d2g.setParam("up2Num", String.valueOf(800));
LongOpsRunner.getLongOpsRunner().runOp(d2g);
所以,我们开始吧。这是每个想要执行长任务(操作 - 操作)的活动都应该实现的接口:
public interface LongOpsActivity {
public void onTaskCompleted(OpResult result);
}
这是一个包含任何任务的任何结果的类:
public class OpResult {
public LongOpsActivity forActivity;
public int opType;
public Object result;
public OpResult(LongOpsActivity forActivity, int opType, Object result){
this.forActivity = forActivity;
this.opType = opType;
this.result = result;
}
}
最后是最重要的部分,单例异步任务类:
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import android.os.AsyncTask;
public class LongOpsRunner extends AsyncTask<Void, OpResult, Void> {
public class Data2Get implements Cloneable {
// one id for each operation
public static final int OpCountNumbers = 1;
public static final int OpCountLetters = 2;
public LongOpsActivity forActivity;
public int opType;
private HashMap<String, String> params = new HashMap<String, String>();
public Data2Get(LongOpsActivity forActivity, int opType) {
this.forActivity = forActivity;
this.opType = opType;
}
public void setParam(String key, String value) {
params.put(key, value);
}
public String getParam(String key) {
return params.get(key);
}
public void clearParams() {
params.clear();
}
@Override
protected Object clone() throws CloneNotSupportedException {
// deep clone
Data2Get myClone = (Data2Get) super.clone();
myClone.clearParams();
for (Entry<String, String> entry : params.entrySet()) {
myClone.setParam(new String(entry.getKey()), new String(entry.getValue()));
}
return myClone;
}
}
private class IntermediateResult extends OpResult {
public IntermediateResult(LongOpsActivity forActivity, int opType, Object result) {
super(forActivity, opType, result);
}
}
// not really needed
private class FinalResult extends OpResult {
public FinalResult(LongOpsActivity forActivity, int opType, Object result) {
super(forActivity, opType, result);
}
}
private final ReentrantLock lock = new ReentrantLock();
private final Condition executeOp = lock.newCondition();
private volatile boolean finished = false;
private volatile boolean waiting = true;
private volatile boolean shouldCancel = false;
private volatile boolean activityHasBeenNotified = true;
private Data2Get startingOpParams = null;
private Data2Get currentOpParams = null;
private FinalResult currentOpResult;
protected Void doInBackground(Void... nothing) {
try {
lock.lockInterruptibly();
do {
waiting = true;
while (waiting) {
executeOp.await();
}
shouldCancel = false;
activityHasBeenNotified = false;
boolean opCancelled = false;
try {
currentOpParams = (Data2Get) startingOpParams.clone();
} catch (CloneNotSupportedException cns) {
// do nothing
}
switch (currentOpParams.opType) {
case Data2Get.OpCountNumbers:
int numberCounter = 0;
int numLoopCount = 0;
while ((!opCancelled) & (numLoopCount <= 5000000)) {
if (!shouldCancel) {
numberCounter = (numberCounter + 1)
% Integer.parseInt(currentOpParams.getParam("up2Num"));
if (numberCounter == 0) {
numLoopCount++;
publishProgress(new IntermediateResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Numbers loop count:" + numLoopCount));
}
} else {
opCancelled = true;
activityHasBeenNotified = true;
}
if (!opCancelled) {
currentOpResult = new FinalResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Numbers loop completed.");
publishProgress(currentOpResult);
}
}
break;
case Data2Get.OpCountLetters:
int letterLoopCount = 0;
char ch = 'a';
while (!opCancelled & (letterLoopCount <= 5000000)) {
if (!shouldCancel) {
ch++;
if (Character.toString(ch).equals(currentOpParams.getParam("up2Letter"))) {
ch = 'a';
letterLoopCount++;
publishProgress(new IntermediateResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Letters loop count:" + letterLoopCount));
}
} else {
opCancelled = true;
activityHasBeenNotified = true;
}
if (!opCancelled) {
currentOpResult = new FinalResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Letters loop completed.");
publishProgress(currentOpResult);
}
}
break;
default:
}
} while (!finished);
lock.unlock();
} catch (InterruptedException e) {
// do nothing
}
return null;
}
public void cancelCurrentOp() {
shouldCancel = true;
}
@Override
protected void onProgressUpdate(OpResult... res) {
OpResult result = res[0];
if (result instanceof IntermediateResult) {
// normal progress update
// use result.forActivity to show something in the activity
} else {
notifyActivityOpCompleted(result);
}
}
public boolean currentOpIsFinished() {
return waiting;
}
public void runOp(Data2Get d2g) {
// Call this to run an operation
// Should check first currentOpIsFinished() most of the times
startingOpParams = d2g;
waiting = false;
executeOp.signal();
}
public void terminateAsyncTask() {
// The task will only finish when we call this method
finished = true;
lock.unlock(); // won't this throw an exception?
}
protected void onCancelled() {
// Make sure we clean up if the task is killed
terminateAsyncTask();
}
// if phone is rotated, use setActivity(null) inside
// onRetainNonConfigurationInstance()
// and setActivity(this) inside the constructor
// and all that only if there is an operation still running
public void setActivity(LongOpsActivity activity) {
currentOpParams.forActivity = activity;
if (currentOpIsFinished() & (!activityHasBeenNotified)) {
notifyActivityOpCompleted(currentOpResult);
}
}
private void notifyActivityOpCompleted(OpResult result) {
if (currentOpParams.forActivity != null) {
currentOpParams.forActivity.onTaskCompleted(result);
activityHasBeenNotified = true;
}
}
private static LongOpsRunner ref;
private LongOpsRunner() {
this.execute();
}
public static synchronized LongOpsRunner getLongOpsRunner() {
if (ref == null)
ref = new LongOpsRunner();
return ref;
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
我希望有人能帮助完成这项工作,因为它不仅对我有用,而且对许多其他人也很有用。谢谢你。