実行中のAsyncTaskをキャンセルする理想的な方法


108

を使用して、バックグラウンドスレッドでリモートオーディオファイルの取得とオーディオファイルの再生操作を実行していますAsyncTaskCancellableプログレスバーは、操作の実行をフェッチする時のために示されています。

AsyncTaskユーザーが操作をキャンセル(決定)したときに実行をキャンセルまたは中止したい。そのような場合に対処するための理想的な方法は何ですか?

回答:


76

ちょうどことを発見AlertDialogs「S boolean cancel(...);私はどこにでも実際に使用して何もしませんしてきました。すごい。
そう...

public class MyTask extends AsyncTask<Void, Void, Void> {

    private volatile boolean running = true;
    private final ProgressDialog progressDialog;

    public MyTask(Context ctx) {
        progressDialog = gimmeOne(ctx);

        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                // actually could set running = false; right here, but I'll
                // stick to contract.
                cancel(true);
            }
        });

    }

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected void onCancelled() {
        running = false;
    }

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

        while (running) {
            // does the hard work
        }
        return null;
    }

    // ...

}

55
runningのブールフラグを作成する代わりに、それを削除してこれをwhile(!isCanceled())にできませんか?
孔子2012

36
onCancelled()に関するドキュメントから:「cancel(boolean)が呼び出され、doInBackground(Object [])が完了した後に UIスレッドで実行されます。」この「後」は、onCancelledでフラグを設定してdoInBackgroundをチェックインしても意味がないことを意味します。
lopek 2013

2
@confuciusは正しいですが、この方法でバックグラウンドスレッドが中断されることはありません。たとえば、画像をアップロードするときに、アップロードプロセスがバックグラウンドで続行され、onPostExecuteが呼び出されなかったとします。
umesh 2014年

1
@DanHulme孔子のコメントではなく、回答で提供されたコードスニペットを参照したと思います(正しい)。
lopek

4
はい、この回答は機能しません。doInBackgroundでは、交換するwhile(running)while(!isCancelled())他の人がコメントの中で、ここで述べてきたように。
matt5784 2015

76

計算をしている場合

  • isCancelled()定期的にチェックする必要があります。

HTTPリクエストを実行している場合

  • HttpGetまたはHttpPostどこかにインスタンスを保存します(例:パブリックフィールド)。
  • を呼び出した後cancel、を呼び出しますrequest.abort()。これはIOException内にスローされますdoInBackground

私の場合、さまざまなAsyncTaskで使用するコネクタクラスがありました。簡単にするために、abortAllRequestsそのクラスに新しいメソッドを追加し、を呼び出しcancelた直後にこのメソッドを呼び出しました。


ありがとう、それは機能しますが、この場合例外を回避するにはどうすればよいですか?
begiPass 2013年

HttpGet.abort()バックグラウンドスレッドから呼び出す必要があります。そうしないと、android.os.NetworkOnMainThreadException
ヒースボーダー14

@wrygiel HTTPリクエストを実行している場合cancel(true)、リクエストを中断しないでください。ドキュメントから:If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Storo

HttpURLConnection.disconnect();
Oded Breiner、2016年

AsyncTaskでCPUを消費する操作がある場合は、を呼び出す必要がありますcancel(true)。私はそれを使用し、それは動作します。
SMMousavi 2016年

20

重要なのは、AsyncTask.cancel()呼び出しがタスクのonCancel関数のみを呼び出すことです。これは、キャンセル要求を処理する場所です。

これは、更新メソッドをトリガーするために使用する小さなタスクです

private class UpdateTask extends AsyncTask<Void, Void, Void> {

        private boolean running = true;

        @Override
        protected void onCancelled() {
            running = false;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            onUpdate();
        }

        @Override
        protected Void doInBackground(Void... params) {
             while(running) {
                 publishProgress();
             }
             return null;
        }
     }

2
これは機能しますが、サーバーの応答を待っているときに論理的には、db操作を実行しただけの場合は、アクティビティへの正しい変更が反映されているはずです。私はそれについてブログを書きました、私の答えを見てください。
Vikas

4
承認された回答のコメントで述べたように、独自のrunningフラグを作成する必要はありません。AsyncTaskには、タスクがキャンセルされたときに設定される内部フラグがあります。交換してくださいwhile (running)while (!isCancelled())developer.android.com/reference/android/os/… したがって、この単純なケースでは、onCancelled()オーバーライドする必要はありません。
ToolmakerSteve

11

シンプル:を使用しないでくださいAsyncTaskAsyncTaskは短時間で終了する(数十秒)ため、キャンセルする必要がない短い操作用に設計されています。「オーディオファイルの再生」は対象外です。通常のオーディオファイルの再生には、バックグラウンドスレッドは必要ありません。


通常のJavaスレッドを使用し、揮発性ブール変数を使用してスレッドの実行を「中止」することをお勧めですか?
Samuh 2010

34
不快なマイクはありませんが、それは受け入れられる答えではありません。AsyncTaskにはキャンセルメソッドがあり、動作するはずです。私の知る限り、そうではありません。ただし、間違っている場合でも、タスクをキャンセルする正しい方法があるはずです。それ以外の場合、メソッドは存在しません。短いタスクでもキャンセルが必要な場合があります-読み込み時にすぐにAsyncTaskを開始するアクティビティがあり、ユーザーがタスクを開いた直後に打ち返した場合、タスクが完了したがコンテキストがないときに、2番目に強制終了が表示されます。 onPostExecuteで使用するために存在します。
エリックミル

10
@クロンダイク:「マイク」が誰なのか私にはわからない。「しかし、それは許容できる答えではありません」-あなたの意見は大歓迎です。「AsyncTaskにはキャンセルメソッドがあり、機能するはずです。」-Javaでのスレッドのキャンセルは、約15年間問題でした。Androidとは何の関係もありません。「強制終了」のシナリオに関しては、ブール変数で解決できます。この変数をテストしonPostExecute()て、作業を続行する必要があるかどうかを確認します。
CommonsWare 2010

1
@Tejaswi Yerukalapudi:自動的に何もしないというだけではありません。この質問で受け入れられた回答を参照してください。
CommonsWare、2011年

10
AsyncTaskのdoInBackgroundでisCancelledメソッドを定期的に確認する必要があります。これは、ドキュメントにすぐそこです:developer.android.com/reference/android/os/...
クリストファー・ペリー

4

これを行う唯一の方法は、isCancelled()メソッドの値を確認し、trueが返されたときに再生を停止することです。


4

これが、AsyncTask
の記述方法です。重要な点は、Thread.sleep(1)を追加することです。

@Override   protected Integer doInBackground(String... params) {

        Log.d(TAG, PRE + "url:" + params[0]);
        Log.d(TAG, PRE + "file name:" + params[1]);
        downloadPath = params[1];

        int returnCode = SUCCESS;
        FileOutputStream fos = null;
        try {
            URL url = new URL(params[0]);
            File file = new File(params[1]);
            fos = new FileOutputStream(file);

            long startTime = System.currentTimeMillis();
            URLConnection ucon = url.openConnection();
            InputStream is = ucon.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);

            byte[] data = new byte[10240]; 
            int nFinishSize = 0;
            while( bis.read(data, 0, 10240) != -1){
                fos.write(data, 0, 10240);
                nFinishSize += 10240;
                **Thread.sleep( 1 ); // this make cancel method work**
                this.publishProgress(nFinishSize);
            }              
            data = null;    
            Log.d(TAG, "download ready in"
                  + ((System.currentTimeMillis() - startTime) / 1000)
                  + " sec");

        } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                returnCode = FAIL;
        } catch (Exception e){
                 e.printStackTrace();           
        } finally{
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                e.printStackTrace();
            }
        }

        return returnCode;
    }

1
非同期タスクで単にcancel(true)を呼び出してisCancelled()を定期的にチェックしても機能することがわかりましたが、タスクの動作によっては、中断されるまでに最大60秒かかる場合があります。Thread.sleep(1)を追加すると、すぐに中断することができます。(非同期タスクはむしろ待機状態になり、すぐに破棄されません)。これをありがとう。
John J Smith

0

グローバルAsyncTaskクラス変数

LongOperation LongOperationOdeme = new LongOperation();

そして、AsyncTaskを中断するKEYCODE_BACKアクション

   @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            LongOperationOdeme.cancel(true);
        }
        return super.onKeyDown(keyCode, event);
    }

わたしにはできる。


0

cancel(true)ソケットやファイルストリームを閉じる、ローカルデータベースにデータを書き込むなど、リソースを解放する必要があるため、非同期タスクを不必要に強制的に中断したくありません。一方、次のような状況に直面しています。非同期タスクが時間の一部を終了するのを拒否します。たとえば、メインアクティビティが閉じられているときに、アクティビティのonPause()メソッド内から非同期タスクの終了を要求する場合があります。したがって、単にを呼び出すだけでは問題ありませんrunning = false。私は混合ソリューションにrunning = false取り掛かる必要があります。両方ともを呼び出し、非同期タスクが完了するまで数ミリ秒待ってから、cancel(false)またはのいずれかを呼び出しますcancel(true)

if (backgroundTask != null) {
    backgroundTask.requestTermination();
    try {
        Thread.sleep((int)(0.5 * 1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (backgroundTask.getStatus() != AsyncTask.Status.FINISHED) {
        backgroundTask.cancel(false);
    }
    backgroundTask = null;
}

副次的な結果として、doInBackground()終了後にonCancelled()メソッドが呼び出されることもあれば、が呼び出されることもありonPostExecute()ます。しかし、少なくとも非同期タスクの終了は保証されています。


競合状態のように見えます。
msangel 2017

0

'10年4月29日のYanchenkoの回答を参照してください。'doInBackground 'の下のコードをAsyncTaskのすべての実行中に複数回実行する必要がある場合、' while(running) 'アプローチの使用は適切です。「doInBackground」の下のコードをAsyncTaskの実行ごとに1回だけ実行する必要がある場合、「doInBackground」の下のすべてのコードを「while(running)」ループでラップしても、バックグラウンドコード(バックグラウンドスレッド)の実行は停止しません。 'while(running)'条件は、whileループ内のすべてのコードが少なくとも1回実行されると評価されるため、AsyncTask自体はキャンセルされます。したがって、(a。)「doInBackground」の下のコードを複数の「while(running)」ブロックに分割するか、(b。)多数の「isCancelled」を実行する必要があります。https://developer.android.com/reference/android/os/AsyncTask.html

したがって、オプション(a。)の場合、Yanchenkoの回答を次のように変更できます。

public class MyTask extends AsyncTask<Void, Void, Void> {

private volatile boolean running = true;

//...

@Override
protected void onCancelled() {
    running = false;
}

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

    // does the hard work

    while (running) {
        // part 1 of the hard work
    }

    while (running) {
        // part 2 of the hard work
    }

    // ...

    while (running) {
        // part x of the hard work
    }
    return null;
}

// ...

オプション(b。)の場合、「doInBackground」のコードは次のようになります。

public class MyTask extends AsyncTask<Void, Void, Void> {

//...

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

    // part 1 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // part 2 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // ...

    // part x of the hard work
    // ...
    if (isCancelled()) {return null;}
}

// ...
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.