问题出在AsyncTask上。根据文档,它允许在不必操作线程和/或处理程序的情况下,在后台执行操作并在UI线程上发布结果。例子继续展示了如何在onPostExecute()中调用一些showDialog()方法,然而,这对我来说似乎完全是牵强附会的,因为显示对话框总是需要引用有效的上下文,而AsyncTask绝不能保留对上下文对象的强引用。
原因很明显:如果触发任务的活动被销毁怎么办?这可能随时发生,例如,因为您翻转了屏幕。如果任务保留了创建它的上下文的引用,那么你不仅保存了一个无用的上下文对象(窗口将已被销毁,并且任何UI交互都会失败并抛出异常!),而且还有可能造成内存泄漏。
除非我的逻辑出现错误,否则这意味着:onPostExecute()是完全无用的,因为如果您没有访问任何上下文,这个方法在UI线程上运行有什么好处呢?你在这里无法做任何有意义的事情。
一种解决方法是不向AsyncTask传递上下文实例,而是传递Handler实例。这样可以避免泄漏的风险,因为Handler松散地绑定了上下文和任务,你可以在它们之间交换消息(对吗?)。但这意味着AsyncTask的前提是错误的,即你不需要烦恼处理程序。同时,这似乎滥用了Handler,因为你在同一个线程上发送和接收消息(你在UI线程上创建它,并在onPostExecute()中通过它发送)。
更糟糕的是,即使使用这种解决方法,当上下文被销毁时,你仍然无法记录所触发的任务。这意味着在重新创建上下文后(例如,在屏幕方向改变后),你必须重新启动所有的任务。这很慢也很浪费。
我的解决方案(在Droid-Fu库中实现)是在唯一的应用程序对象上维护从组件名称到它们当前实例的WeakReference映射。每当启动一个AsyncTask时,它就会在该映射中记录调用上下文,并在每个回调时从该映射中获取当前上下文实例。这确保你永远不会引用过期的上下文实例,同时在回调中始终有一个有效的上下文,因此你可以在那里进行有意义的UI工作。这也不会泄漏,因为引用是弱引用,并且在给定组件的任何实例不存在时被清除。
尽管如此,这仍然是一种复杂的解决方法,并需要对Droid-Fu库中的某些类进行子类化,使其成为相当侵入性的方法。
现在我只是想知道:我是不是漏掉了什么重要的东西,还是AsyncTask真的存在很大的缺陷?你们使用它的经验如何?你们是如何解决这些问题的?
谢谢你的回答。