私のアクティビティの1つにAsyncTaskを実装しました。
_ performBackgroundTask asyncTask = new performBackgroundTask();
asyncTask.execute();
_
ここで、「キャンセル」ボタン機能を実装する必要があるため、実行中のタスクの実行を停止する必要があります。実行中のタスク(バックグラウンドタスク)を停止する方法がわかりません。
AsyncTaskを強制的にキャンセルするにはどうすればよいですか?
同じCancel()
メソッドについては見つけましたが、cancel(boolean mayInterruptIfRunning)
を呼び出してもバックグラウンドプロセスの実行が必ずしも停止するわけではないことがわかりました。起こりそうなことは、AsyncTaskがonCancelled()を実行し、完了時にonPostExecute()を実行しないということだけです。
時々isCancelled()
をチェックするだけです:
protected Object doInBackground(Object... x) {
while (/* condition */) {
// work...
if (isCancelled()) break;
}
return null;
}
AsyncTask
でcancel()
を呼び出します。これが実際に何かをキャンセルするかどうかは、あなたがしていることに少し依存します。 Romain Guyを引用するには:
Cancel(true)を呼び出すと、割り込みがバックグラウンドスレッドに送信され、割り込み可能なタスクに役立つ場合があります。それ以外の場合は、doInBackground()メソッドでisCancelled()を定期的に確認するようにしてください。 code.google.com/p/shelvesでこの例を見ることができます。
非同期タスクで何をしているのかに本当に依存します。
多数のファイルを処理するループの場合は、isCanceled()フラグが立てられているかどうかを各ファイルの後にチェックし、そうであればループを中断できます。
非常に長い操作を実行する1行のコマンドの場合、できることはあまりありません。
最善の回避策は、asynctaskのcancelメソッドを使用せず、独自のcancelFlagブール値を使用することです。その後、postExecuteでこのcancelFlagをテストして、結果をどう処理するかを決定できます。
コメントに記載されているisCancelled() always returns false even i call asynctask.cancel(true);
は、アプリを閉じると特に有害ですが、AsyncTaskは動作し続けます。
これを解決するために、提案した_Jacob Nordfalk
_コードを次のように変更しました。
_protected Object doInBackground(Object... x) {
while (/* condition */) {
// work...
if (isCancelled() || (FlagCancelled == true)) break;
}
return null;
}
_
メインアクティビティに次を追加しました。
_@Override
protected void onStop() {
FlagCancelled = true;
super.onStop();
}
_
私のAsyncTaskはビューの1つのプライベートクラスであるため、現在の実際のフラグ値をAsyncTaskに通知するには、フラグの取得者または設定者が必要でした。
私の複数のテスト(AVD Android 4.2.2、Api 17)は、AsyncTaskが既にdoInBackground
を実行している場合、isCancelled()
はnoで反応することを示しました。キャンセルしようとする方法(つまり、falseのまま)で、たとえばmViewGroup.removeAllViews();
中またはOnDestroy
のMainActivity
中に、それぞれがビューのデタッチにつながる
_ @Override
protected void onDetachedFromWindow() {
mAsyncTask.cancel(false); // and the same result with mAsyncTask.cancel(true);
super.onDetachedFromWindow();
}
_
導入されたFlagCancelled
のおかげでdoInBackground()
の強制停止に成功した場合、onPostExecute()
が呼び出されますが、onCancelled()
もonCancelled(Void result)
(APIレベル11以降)は呼び出されません。 (理由はわからないが、それらは呼び出されるべきであり、onPostExecute()
はそうすべきではない、「cancel()メソッドを呼び出すと、onPostExecute(Object)が呼び出されないことが保証される。」-IdleSun
、 同様の質問に答える )。
一方、同じAsyncTaskがキャンセルする前にdoInBackground()
を開始していなかった場合、すべてが正常であり、isCancelled()
がtrueに変わり、チェックインすることができます
_@Override
protected void onCancelled() {
Log.d(TAG, String.format("mAsyncTask - onCancelled: isCancelled = %b, FlagCancelled = %b", this.isCancelled(), FlagCancelled ));
super.onCancelled();
}
_
AsyncTaskは長時間実行される操作には使用すべきではありませんが、応答しないタスク(応答しないHTTP呼び出しなど)でキャッチされる場合があります。その場合、AsyncTaskをキャンセルする必要があります。
これを行うには挑戦が必要です。 1. AsyncTaskで表示される通常の進行ダイアログは、ユーザーが[戻る]ボタンを押したときにAsyncTaskで最初にキャンセルされるものです。 2. AsyncTaskはdoInBackgroundメソッド内にある場合があります
ProgressDialogでdismissDialogListernerを作成することにより、ユーザーは[戻る]ボタンを押して、実際にAsycnTaskを無効にし、ダイアログ自体を閉じることができます。
以下に例を示します。
public void openMainLobbyDoor(String username, String password){
if(mOpenDoorAsyncTask == null){
mOpenDoorAsyncTask = (OpenMainDoor) new OpenMainDoor(username, password, Posts.API_URL,
mContext, "Please wait while I unlock the front door for you!").execute(null, null, null);
}
}
private class OpenMainDoor extends AsyncTask<Void, Void, Void>{
//declare needed variables
String username, password, url, loadingMessage;
int userValidated;
boolean canConfigure;
Context context;
ProgressDialog progressDialog;
public OpenMainDoor(String username, String password, String url,
Context context, String loadingMessage){
userValidated = 0;
this.username = username;
this.password = password;
this.url = url;
this.context = context;
this.loadingMessage = loadingMessage;
}
/**
* used to cancel dialog on configuration changes
* @param canConfigure
*/
public void canConfigureDialog(boolean canConfigure){
this.canConfigure = canConfigure;
}
@Override
protected void onPreExecute(){
progressDialog = new ProgressDialog(this.context);
progressDialog.setMessage(loadingMessage);
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mOpenDoorAsyncTask.cancel(true);
}
});
progressDialog.show();
this.canConfigure = true;
}
@Override
protected Void doInBackground(Void... params) {
userValidated = Posts.authenticateNTLMUserLogin(username, password, url, context);
while(userValidated == 0){
if(isCancelled()){
break;
}
}
return null;
}
@Override
protected void onPostExecute(Void unused){
//determine if this is still attached to window
if(canConfigure)
progressDialog.dismiss();
if(userValidated == 1){
saveLoginValues(username, password, true);
Toast.makeText(context, R.string.main_login_pass, Toast.LENGTH_SHORT).show();
}else{
saveLoginValues(username, password, false);
Toast.makeText(context, R.string.main_login_fail, Toast.LENGTH_SHORT).show();
}
nullifyAsyncTask();
}
@Override
protected void onCancelled(){
Toast.makeText(context, "Open door request cancelled!", Toast.LENGTH_SHORT).show();
nullifyAsyncTask();
}
}
グローバル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);
}
わたしにはできる。