web-dev-qa-db-ja.com

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

AsyncTaskを使用して、バックグラウンドスレッドでリモートオーディオファイルフェッチおよびオーディオファイル再生操作を実行しています。フェッチ操作の実行中は、Cancellableプログレスバーが表示されます。

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

107
Samuh

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;
    }

    // ...

}
78
yanchenko

計算をしている場合

  • isCancelled()を定期的に確認する必要があります。

HTTPリクエストを行う場合

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

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

76
wrygiel

問題は、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;
        }
     }
20
DonCroco

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

11
CommonsWare

これは私が私の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;
    }
4
Andrew Chan

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

4
dbyrne

グローバル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
Göksel Güren

10年4月29日のYanchenkoの回答を参照すると、AsyncTaskの実行ごとに「doInBackground」の下のコードを複数回実行する必要がある場合、「while(running)」アプローチの使用は適切です。 「doInBackground」の下のコードをAsyncTaskの実行ごとに1回だけ実行する必要がある場合、「doInBackground」の下のすべてのコードを「while(running)」ループでラップしても、バックグラウンドコード(バックグラウンドスレッド)の実行は停止しません。 「while(running)」条件は、whileループ内のすべてのコードが少なくとも1回実行されると評価されるため、AsyncTask自体はキャンセルされます。したがって、(a。)「doInBackground」の下のコードを複数の「while(running)」ブロックに分割するか、(b。)「doInBackground」コード全体で多数の「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;}
}

// ...
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()が呼び出されることもあります。ただし、少なくとも非同期タスクの終了は保証されています。

0
Piovezan