【发布时间】:2011-03-22 09:52:21
【问题描述】:
我已经对这个问题进行了几个月的调查,提出了不同的解决方案,但我对此并不满意,因为它们都是大规模的黑客攻击。我仍然无法相信一个在设计上有缺陷的类进入了框架并且没有人在谈论它,所以我想我一定是遗漏了一些东西。
问题在于AsyncTask。根据文档它
"允许执行背景 操作并发布结果 无需操作的 UI 线程 线程和/或处理程序。”
然后该示例继续展示如何在onPostExecute() 中调用一些示例性showDialog() 方法。然而,这对我来说似乎完全做作,因为显示对话框总是需要对有效Context 的引用,并且 AsyncTask 绝不能持有对上下文对象的强引用 em>。
原因很明显:如果触发任务的活动被破坏怎么办?这可能一直发生,例如因为你翻转了屏幕。如果任务持有对创建它的上下文的引用,那么您不仅持有一个无用的上下文对象(窗口将被销毁,并且 任何 UI 交互都将失败并出现异常! ),您甚至可能会造成内存泄漏。
除非我的逻辑在这里有缺陷,否则这会转化为:onPostExecute() 完全没用,因为如果您无法访问任何上下文,那么在 UI 线程上运行此方法有什么好处?你不能在这里做任何有意义的事情。
一种解决方法是不将上下文实例传递给 AsyncTask,而是传递一个 Handler 实例。这行得通:由于 Handler 松散地绑定了上下文和任务,因此您可以在它们之间交换消息而不会冒泄漏的风险(对吗?)。但这意味着 AsyncTask 的前提,即您不需要处理处理程序,是错误的。这似乎也是在滥用 Handler,因为您在同一个线程上发送和接收消息(您在 UI 线程上创建它并在 onPostExecute() 中通过它发送,它也在 UI 线程上执行)。
最重要的是,即使使用这种解决方法,您仍然会遇到这样的问题,即当上下文被破坏时,您没有记录它触发的任务。这意味着您必须在重新创建上下文时重新启动任何任务,例如屏幕方向更改后。这既慢又浪费。
我对此的解决方案(如implemented in the Droid-Fu library)是维护WeakReferences 从组件名称到它们在唯一应用程序对象上的当前实例的映射。每当启动 AsyncTask 时,它都会在该映射中记录调用上下文,并且在每次回调时,它都会从该映射中获取当前上下文实例。这可确保您永远不会引用过时的上下文实例并且您始终可以访问回调中的有效上下文,以便您可以在那里进行有意义的 UI 工作。它也不会泄漏,因为引用很弱,并且当给定组件的实例不再存在时会被清除。
不过,这是一个复杂的解决方法,并且需要对一些 Droid-Fu 库类进行子类化,这使得这是一种相当侵入性的方法。
现在我只想知道:我只是大量遗漏了某些东西还是 AsyncTask 真的完全有缺陷?您使用它的经验如何?您是如何解决这些问题的?
感谢您的意见。
【问题讨论】:
-
如果您好奇,我们最近在点火核心库中添加了一个名为 IgnitedAsyncTask 的类,它使用 Dianne 概述的连接/断开模式在所有回调中添加了对类型安全上下文访问的支持以下。它还允许抛出异常并在单独的回调中处理它们。见github.com/kaeppler/ignition-core/blob/master/src/com/github/…
-
这个question也是相关的。
-
我将异步任务添加到一个数组列表中,并确保在某个时间点将它们全部关闭。
标签: android concurrency handler android-asynctask