1

私の目標は、AsyncTask を持つことです。

  • 複数回実行できます (もちろん一度に 1 つのタスク)
  • 現在のタスクをキャンセルできます
  • あらゆるアクティビティで使用できます
  • 多くの異なるタスクを実行できます
  • 画面の回転(または電話など)に問題はありません

それを達成するために、以下に示すクラスを作成しました。しかし、スレッドに関する私の経験 (および理解) は非常に限られています。そして、複数のスレッドをデバッグする方法がわからないので、これが機能するかどうかを知る方法はありません。だから私が本当に求めているのは、このコードは大丈夫ですか?

現在これを使用しているコードはないため、使用例を次に示します。

Data2Get d2g = new Data2Get(this, Data2Get.OpCountNumbers);
d2g.setParam("up2Num", String.valueOf(800));
LongOpsRunner.getLongOpsRunner().runOp(d2g);

では、どうぞ。これは、長いタスク (操作 - op) を実行するすべてのアクティビティが実装する必要があるインターフェイスです。

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

試してみてくださいLoadersAsyncTask私は単純なsからsに切り替えました、AsyncTaskLoaderそしてそれらは多くの問題を解決します。ローダーをスタンドアロンクラスとして実装する場合、特に古いものの最大の問題であるローテーションに関しては、すべての要件を満たしますAsyncTask

于 2012-04-09T11:56:07.217 に答える