【发布时间】:2012-04-15 16:22:05
【问题描述】:
从doInBackground() 内部调用AsyncTask.cancel(true) 后,Android 调用onPostExecute(),而不是调用onCancelled()。但是as per the documentation:
调用这个方法会导致
onCancelled(Object)被调用 在doInBackground(Object[])返回后的 UI 线程上。 调用这个 方法保证onPostExecute(Object)永远不会被调用。
这是 Android 中的错误吗?
更多观察:
- 从任一线程调用
cancel(false)的工作方式在 文档。 - 从 UI 任务调用
cancel(true)不会调用onPostExecute(),也不会抛出下面 logcat 跟踪中看到的InterruptedException。 - 从任何线程调用
cancel(false/true)有时甚至在doInBackground()返回之前调用onCancelled()。这显然违反了文档,which states:
调用此方法将导致 onCancelled(Object) 被调用 在 UI 线程上
doInBackground(Object[])返回后。
代码:(在 Android 2.2 设备上测试)
protected Void doInBackground(Void... params) {
Log.d(TAG, "started doInBackground()");
while (!isCancelled()) {
boolean ret = cancel(true);
Log.d(TAG, "cancel() returned: " + ret);
}
Log.d(TAG, "returning from doInBackground()");
return null;
}
Logcat 输出
04-15 21:38:55.519: D/MyTask(27597): started doInBackground()
04-15 21:38:55.589: W/AsyncTask(27597): java.lang.InterruptedException
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1254)
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:219)
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.FutureTask.get(FutureTask.java:82)
04-15 21:38:55.589: W/AsyncTask(27597): at android.os.AsyncTask$3.done(AsyncTask.java:196)
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.FutureTask$Sync.innerCancel(FutureTask.java:293)
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.FutureTask.cancel(FutureTask.java:75)
04-15 21:38:55.589: W/AsyncTask(27597): at android.os.AsyncTask.cancel(AsyncTask.java:325)
04-15 21:38:55.589: W/AsyncTask(27597): at com.example.test.TestActivity$MyTask.doInBackground(TestActivity.java:31)
04-15 21:38:55.589: W/AsyncTask(27597): at com.example.test.TestActivity$MyTask.doInBackground(TestActivity.java:1)
04-15 21:38:55.589: W/AsyncTask(27597): at android.os.AsyncTask$2.call(AsyncTask.java:185)
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
04-15 21:38:55.589: W/AsyncTask(27597): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
04-15 21:38:55.589: W/AsyncTask(27597): at java.lang.Thread.run(Thread.java:1096)
04-15 21:38:55.589: D/MyTask(27597): cancel() returned: true
04-15 21:38:55.589: D/MyTask(27597): returning from doInBackground()
04-15 21:38:55.659: D/MyTask(27597): onPostExecute()
【问题讨论】:
-
为什么要从
doInBackground中取消AsyncTask?那没有意义。cancel(...)方法故意允许工作线程之外的代码(换句话说,在 UI 线程上)停止执行。如果doInBackground中的代码出于某种原因需要自行终止,那么它应该只是return。如果您不希望onPostExecute(...)由于伪取消而执行某些操作,则返回false否则返回true -
@MisterSquonk,“cancel(...) 方法故意允许工作线程之外的代码停止执行。”文档并没有说它只能从 UI 线程调用。为什么不重用
onCancelled()的现有代码,而不是使用难看的变通方法? -
同意,文档并没有说它应该只从 UI 线程调用,但这似乎是最合乎逻辑的。我这么说的原因是文档确实说
doInBackground应该定期检查isCancelled()以查看它是否需要停止自己的执行(作为调用cancel的结果)。如果doInBackground调用cancel是正常的,那么isCancelled()的意义何在? -
@MisterSquonk,很公平。仍然为什么会出现这种人为的怪异行为?另外,我在“更多观察”下添加了第 3 点。
-
我一直在寻找适用于 Android v2.2 的
AsyncTask的源代码...grepcode.com/file/repository.grepcode.com/java/ext/… 但我无法找到答案。我可以看到AsyncTask使用java.util.concurrent.FutureTask这就是引发您发布的异常的原因。我暂时找不到它的来源。我怀疑某处有一个小错误,我同意优雅地使用onCancelled会很好。也许编写自己的AsyncTask版本可能是答案。