web-dev-qa-db-ja.com

Android:非同期タスクのキャンセル

非同期タスクを使用して画像をアップロードし、結果を取得します。

画像のアップロード中に、次のようなonPreExecute()メソッドで記述された進行ダイアログが表示されます。

    protected void onPreExecute() { 
         uploadingDialog = new ProgressDialog(MyActivity.this); 
         uploadingDialog.setMessage("uploading"); 
         uploadingDialog.setCancelable(true);
         uploadingDialog.show();
    }

[戻る]ボタンを押すと、明らかにsetCancelable(true)のためにダイアログが消えます。

ただし、(明らかに)非同期タスクは停止しません。

どうすればこれを修正できますか?戻るボタンを押したときにダイアログと非同期タスクの両方をキャンセルしたい。何か案は?

編集:ソリューションが見つかりました。以下の回答をご覧ください。

56
MScott

ソリューションの検索:次のように、Dialog.show()をアップロードする前にアクションリスナーを追加しました。

    uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
          public void onCancel(DialogInterface dialog) {
              myTask.cancel(true);
              //finish();
          }
    });

そのようにして、戻るボタンを押すと、上記のOnCancelListenerはダイアログとタスクの両方をキャンセルします。また、バックプレスでアクティビティ全体を終了する場合は、finish()を追加できます。非同期タスクを次のような変数として宣言することを忘れないでください。

    MyAsyncTask myTask=null;

次のように非同期タスクを実行します。

    myTask = new MyAsyncTask();
    myTask.execute();
32
MScott

SDKから:

タスクをキャンセルする

タスクは、cancel(boolean)を呼び出すことでいつでもキャンセルできます。このメソッドを呼び出すと、isCancelled()の後続の呼び出しでtrueが返されます。

このメソッドを呼び出した後、doInBackground(Object [])が戻った後、onPostExecute(Object)の代わりにonCancelled(Object)が呼び出されます。

タスクができるだけ早くキャンセルされるようにするには、可能であれば(たとえばループ内で)doInBackground(Object [])からisCancelled()の戻り値を定期的にチェックする必要があります。

したがって、コードはダイアログリスナーに適しています。

uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
    public void onCancel(DialogInterface dialog) {
        myTask.cancel(true);
        //finish();
    }
});

ここで、SDKで前述したように、onPreExecute()メソッド内でisCancelled()を確認する必要があるため、タスクがキャンセルされたかどうかを確認する必要があります。

例えば:

if (isCancelled()) 
    break;
else
{
   // do your work here
}
101
Paresh Mayani

私はこれを理解するのにしばらく時間を費やしましたが、私が望んだのは、それを行う方法の簡単な例だけでした。これはライブラリを更新するコードであり、更新された本の数を示す進行ダイアログがあり、ユーザーがダイアログを閉じるとキャンセルされます。

private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
    private ProgressDialog dialog = new ProgressDialog(Library.this);
    private int total = Library.instance.appState.getAvailableText().length;
    private int count = 0;

    //Used as handler to cancel task if back button is pressed
    private AsyncTask<Void, Integer, Boolean> updateTask = null;

    @Override
    protected void onPreExecute(){
        updateTask = this;
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setOnDismissListener(new OnDismissListener() {               
            @Override
            public void onDismiss(DialogInterface dialog) {
                updateTask.cancel(true);
            }
        });
        dialog.setMessage("Updating Library...");
        dialog.setMax(total);
        dialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... arg0) {
            for (int i = 0; i < appState.getAvailableText().length;i++){
                if(isCancelled()){
                    break;
                }
                //Do your updating stuff here
            }
        }

    @Override
    protected void onProgressUpdate(Integer... progress){
        count += progress[0];
        dialog.setProgress(count);
    }

    @Override
    protected void onPostExecute(Boolean finished){
        dialog.dismiss();
        if (finished)
            DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
        else 
            DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
    }
}
10
odiggity

アクティビティにいくつかのメンバー変数を作成します

YourAsyncTask mTask;
Dialog mDialog;

これらをダイアログとタスクに使用します。

onPause()で単に呼び出す

if(mTask!=null) mTask.cancel(); 
if(mDialog!=null) mDialog.dismiss();
9
jkhouw1

コードを改善したいと思います。 aSyncTaskを実行すると、onCancelled()aSyncTaskのコールバックメソッド)が自動的に呼び出され、progressBarDialogを非表示にできます。

このコードも含めることができます:

public class information extends AsyncTask<String, String, String>
    {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... arg0) {
            return null;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            this.cancel(true);
        }

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

        @Override
        protected void onCancelled() {
            Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
            dialog.hide(); /*hide the progressbar dialog here...*/
            super.onCancelled();
        }

    }
5
Rahul Raina

AsyncTaskを使用するほとんどの場合、ビジネスロジックはUIではなく、分離されたビジネスクラスにあります。その場合、doInBackground()でループを作成できませんでした。例は、サービスを消費し、データを次々に保持する同期プロセスです。

キャンセルを処理できるように、タスクをビジネスオブジェクトに渡します。私のセットアップは次のようなものです。

public abstract class MyActivity extends Activity {

    private Task mTask;
    private Business mBusiness;

    public void startTask() {
        if (mTask != null) {
            mTask.cancel(true);
        }
        mTask = new mTask();
        mTask.execute();
    }
}

protected class Task extends AsyncTask<Void, Void, Boolean> {
    @Override
    protected void onCancelled() {
        super.onCancelled();

        mTask.cancel(true);

        // ask if user wants to try again
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        return mBusiness.synchronize(this);
    }

    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);

        mTask = null;

        if (result) {
            // done!
        }
        else {
            // ask if user wants to try again
        }
    }
}

public class Business {
    public boolean synchronize(AsyncTask<?, ?, ?> task) {
        boolean response = false;
        response = loadStuff(task);

        if (response)
            response = loadMoreStuff(task);

        return response;
    }

    private boolean loadStuff(AsyncTask<?, ?, ?> task) {
        if (task != null && task.isCancelled()) return false;

        // load stuff

        return true;
    }
}
3
saulobrito

AsyncTaskをキャンセルする方法

完全な答えはこちら- Android AsyncTask Example

AsyncTaskは、現在実行中のタスクを終了するための、より優れたキャンセル戦略を提供します。

cancel(boolean mayInterruptIfitRunning)

myTask.cancel(false)-isCancelledがtrueを返すようにします。タスクをキャンセルするのに役立ちます。

myTask.cancel(true)– isCancelled()がtrueを返すようにし、バックグラウンドスレッドを中断し、リソースを解放します。

バックグラウンドスレッドでthread.sleep()メソッドが実行されている場合、cancel(true)はその時点でバックグラウンドスレッドを中断します。ただし、cancel(false)はそれを待機し、そのメソッドが完了するとタスクをキャンセルします。

Cancel()を呼び出し、doInBackground()がまだ実行を開始していない場合。 onCancelled()が呼び出されます。

Cancel(…)を呼び出した後、doInbackground()のisCancelled()によって返される値を定期的に確認する必要があります。以下に示すように。

protected Object doInBackground(Params… params)  { 
 while (condition)
{
 ...
if (isCancelled()) 
break;
}
return null; 
}
2
Thomas Daniel

同様の問題がありました-基本的に、ユーザーがアクティビティを破棄した後、非同期タスクでNPEを取得していました。 Stack Overflowの問題を調査した後、次のソリューションを採用しました。

volatile boolean running;

public void onActivityCreated (Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    running=true;
    ...
    }


public void onDestroy() {
    super.onDestroy();

    running=false;
    ...
}

次に、非同期コードで定期的に「実行中」をチェックします。私はこれをストレステストしました、そして今、私は私の活動を「破る」ことができません。これは完全に機能し、SOで見たいくつかのソリューションよりも単純であるという利点があります。

2
IanB

キャンセルを要求することはできますが、実際にキャンセルすることはできません。こちらをご覧ください answer

2
advantej