web-dev-qa-db-ja.com

AsyncTaskを数回実行します

私のアクティビティでは、AsyncTaskから拡張されるクラスと、そのAsyncTaskのインスタンスであるパラメーターを使用します。 mInstanceOfAT.execute("")を呼び出すと、すべて問題ありません。しかし、AsyncTaskを再度呼び出す更新ボタンを押すと、アプリがクラッシュします(ネットワークジョブが機能しなかった場合)。その後、原因は言う例外が表示されます

タスクを実行できません:タスクは既に実行されています(タスクは1回しか実行できません)

Asyctaskのインスタンスに対してcancel(true)を呼び出そうとしましたが、どちらも機能しません。これまでの唯一の解決策は、Asyntaskの新しいインスタンスを作成することです。それは正しい方法ですか?

ありがとう。

127
Dayerman

AsyncTaskインスタンスは1回しか使用できません。

代わりに、new MyAsyncTask().execute("");のようなタスクを呼び出すだけです

AsyncTask APIドキュメントから:

スレッド化ルール

このクラスが適切に機能するためには、いくつかのスレッド化ルールに従う必要があります。

  • タスクインスタンスはUIスレッドで作成する必要があります。
  • execute(Params ...)は、UIスレッドで呼び出す必要があります。
  • OnPreExecute()、onPostExecute(Result)、doInBackground(Params ...)、onProgressUpdate(Progress ...)を手動で呼び出さないでください。
  • タスクは1回しか実行できません(2回目の実行が試行されると例外がスローされます)
217
Steve Prentice

ASyncTaskの発火して忘れられたインスタンスの理由は、Steve Prenticeの答えでかなり詳しく説明されています-ただし、実行回数に制限されていますASyncTaskを使用すると、スレッドの実行中に好きなことを自由に行うことができます...

doInBackground()内のループ内に実行可能コードを配置し、同時ロックを使用して各実行をトリガーします。 publishProgress()/ onProgressUpdate()を使用して結果を取得できます。

例:

class GetDataFromServerTask extends AsyncTask<Input, Result, Void> {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition tryAgain = lock.newCondition();
    private volatile boolean finished = false;

    @Override
    protected Void doInBackground(Input... params) {

        lock.lockInterruptibly();

        do { 
            // This is the bulk of our task, request the data, and put in "result"
            Result result = ....

            // Return it to the activity thread using publishProgress()
            publishProgress(result);

            // At the end, we acquire a lock that will delay
            // the next execution until runAgain() is called..
            tryAgain.await();

        } while(!finished);

        lock.unlock();
    }

    @Override
    protected void onProgressUpdate(Result... result) 
    {
        // Treat this like onPostExecute(), do something with result

        // This is an example...
        if (result != whatWeWant && userWantsToTryAgain()) {
            runAgain();
        }
    }

    public void runAgain() {
        // Call this to request data from the server again
        tryAgain.signal();
    }

    public void terminateTask() {
        // The task will only finish when we call this method
        finished = true;
        lock.unlock();
    }

    @Override
    protected void onCancelled() {
        // Make sure we clean up if the task is killed
        terminateTask();
    }
}

もちろん、これはASyncTaskの従来の使用法よりもやや複雑であり、実際の進行状況レポートのためにpublishProgress()の使用を放棄します。ただし、メモリが懸念される場合、このアプローチでは、実行時にヒープに残るのは1つのASyncTaskのみになります。

28
seanhodges

同じ問題がありました。私の場合、onCreate()onResume(でやりたいタスクがあります。そこで、Asynctaskを静的にし、そこからインスタンスを取得しました。今でも同じ問題があります。

したがって、onPostExecute()で行ったことは次のとおりです。

instance = null;

私のインスタンスがnullではないことを静的なgetInstanceメソッドでチェックインすることを忘れないでください。

if (instance == null){
    instance = new Task();
}
return instance;

PostExecuteのメソッドは、インスタンスを空にして再作成します。もちろん、これはクラスの外で行うことができます。

2
SamuelD

回転タスクを静的にしたため、回転の変更時にUIスレッドにアタッチ、デタッチ、および再アタッチできました。しかし、あなたの質問に戻るために、私は、スレッドが実行されているかどうかを確認するためのフラグを作成します。スレッドを再起動したい場合は、警告が表示されている場合、ローテーションタスクが実行されているかどうかを確認します。そうでない場合は、nullにしてから新しいものを作成します。これにより、表示されているエラーが回避されます。さらに、正常に完了すると、完了したローテーション認識タスクを無効にして、再度実行できるようにします。

1
Arnab C.

はい、それは本当です、ドキュメントは1つのAsyntaskしか実行できないと言います。

それを使用する必要があるたびに、インスタンス化する必要があります:

// Any time if you need to call her
final FirmwareDownload fDownload = new FirmwareDownload();
fDownload.execute("your parameter");

static class FirmwareDownload extends AsyncTask<String, String, String> {
}
0
Victor Ruiz C.