1

我的目标是有一个 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();
    }

}

我希望有人能帮助完成这项工作,因为它不仅对我有用,而且对许多其他人也很有用。谢谢你。

4

1 回答 1

0

试试Loaders。我从 simple AsyncTasks 切换到AsyncTaskLoaders ,他们解决了很多问题。如果您将 Loader 实现为独立类,它将满足您的所有要求,尤其是当涉及到旋转时,这是 old 的最大问题AsyncTask

于 2012-04-09T11:56:07.217 回答