取消执行AsyncTask的理想方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了取消执行AsyncTask的理想方法相关的知识,希望对你有一定的参考价值。
我正在使用AsyncTask
在后台线程中运行远程音频文件获取和音频文件播放操作。在获取操作运行的时间显示Cancellable
进度条。
当用户取消(决定)操作时,我想取消/中止AsyncTask
运行。处理这种情况的理想方式是什么?
刚刚发现AlertDialogs
的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;
}
// ...
}
如果你正在进行计算:
- 你必须定期检查
isCancelled()
。
如果您正在执行HTTP请求:
- 保存你的
HttpGet
或HttpPost
的实例(例如公共领域)。 - 在致电
cancel
后,请致电request.abort()
。这将导致IOException
被扔进你的doInBackground
。
在我的例子中,我有一个连接器类,我在各种AsyncTasks中使用它。为了简单起见,我在该类中添加了一个新的abortAllRequests
方法,并在调用cancel
后直接调用此方法。
问题是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;
}
}
简单:不要使用AsyncTask
。 AsyncTask
专为短时间操作而设计,可快速结束(数十秒),因此无需取消。 “音频文件播放”不符合条件。您甚至不需要后台线程来播放普通音频文件。
唯一的方法是检查isCancelled()方法的值,并在返回true时停止播放。
这就是我编写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;
}
我们的全局AsyncTask类变量
LongOperation LongOperationOdeme = new LongOperation();
和KEYCODE_BACK动作中断AsyncTask
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
LongOperationOdeme.cancel(true);
}
return super.onKeyDown(keyCode, event);
}
这个对我有用。
我不喜欢用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()
。但至少可以保证异步任务终止。
参考Yanchenko在2010年4月29日的回答:当每次执行AsyncTask期间必须多次执行'doInBackground'下的代码时,使用'while(running)'方法很简洁。如果'doInBackground'下的代码每次执行AsyncTask只需要执行一次,那么在'while(running)'循环中将所有代码包装在'doInBackground'下将不会阻止后台代码(后台线程)运行时AsyncTask本身被取消,因为只有在while循环中的所有代码至少执行一次后才会评估'while(running)'条件。因此,您应该(a。)将“doInBackground”下的代码分解为多个“while(运行)”块或(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;}
}
// ...
以上是关于取消执行AsyncTask的理想方法的主要内容,如果未能解决你的问题,请参考以下文章
在 Asynctask 的 onPostExecute() 方法中更改片段
差异b / w在AsyncTask Android中取消(true)和取消(false)