我有一个简单的Android应用程序,它使用AsyncTasks进行I / O.一个常见的模式:
根据AsyncTask的文档,完成UI更新的正确方法是覆盖AsyncTask类中的onPostExecute - 这将在执行后在UI线程上调用,因此可以触摸小部件等。
但是,对我来说,onPostExecute应该对UI元素有任何形式的硬引用似乎是错误的。我更愿意将I / O任务和UI代码分开。相反,这似乎是一个明显的情况,我应该将不透明的回调传递给AsyncTask - 回调保留对UI元素的引用,因此我们在代码中保持隔离和可重用性。一个经典的委托模式(或许是听众,事件等,这里有很多选项)。
举个例子,下面的代码对我来说似乎不对:
class QueryJobsDBTask extends AsyncTask<Void, Void, ArrayList<ContentValues>> {
@Override
protected void onPostExecute(ArrayList<ContentValues> freshJobsData) {
someList.clear();
someList.addAll(freshJobsData);
// BUG why does my DB query class hold UI references?
someAdapter.notifyDataSetChanged();
}
经过一番研究,看起来Handler类是在这里完成委托模式最直接,最轻量级的方法。我可以为I / O编写可重用的AsyncTasks,并通过Handler实例在每个实例的基础上指定上下文UI更新回调。所以我已经实现了这个新的基于Handler的基类
public abstract class HandlerAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private Handler preExecuteHandler, postExecuteHandler;
public void setPreExecuteHandler(Handler preExecuteHandler) {
this.preExecuteHandler = preExecuteHandler;
}
public void setPostExecuteHandler(Handler postExecuteHandler) {
this.postExecuteHandler = postExecuteHandler;
}
@Override
protected void onPreExecute() {
if (preExecuteHandler != null) {
preExecuteHandler.sendMessage(Message.obtain());
}
}
@Override
protected void onPostExecute(Result result) {
if (postExecuteHandler != null) {
Message msg = Message.obtain();
msg.obj = result;
postExecuteHandler.sendMessage(msg);
}
}
}
瞧,我的所有I / O任务现在都已经从UI正确分区 - 我仍然可以通过Handler实例在需要时指定简单的UI更新回调。这看起来简单,灵活,优于我......所以当然我想知道我错过了什么。
当前的框架解决方案如何优越?这种方法存在一些重大缺陷吗?据我所知,代码执行和线程的拓扑结构在运行时完全相同,只是代码耦合更宽松(并且堆栈上有一些额外的帧)。
答案 0 :(得分:2)
这不仅仅是目的和优势的优越性问题。用例。 AsyncTasks通常在活动中编写为私有类(或匿名内联声明),因此继承了Activity对各种需要更新的UI元素的引用。
如果AsyncTask具有足够的大小和/或复杂性,应该将其拉出到自己的类中,并且可以被其他类重用,那么使用Handler进行更好的去耦是一个好主意。它只是通常没有必要,因为AsyncTask正在完成特定于定义它的Activity的东西,而对于简单的,相应的处理程序代码甚至可能比整个AsyncTask本身更大。
答案 1 :(得分:1)
这是一个优雅的解决方案,用于在小项目中隔离UI /后台任务,尽管传递Runnables更加优雅。请记住,AsyncTask是一个围绕Thread / Handler的包装器,所以你加倍了已经在幕后进行的线程消息传递。这里的缺陷是,如果你设计AsyncTasks是可重用的,你需要确保你运行的IO都是线程安全的,因为各种AsyncTasks之间没有关于谁是活动的或访问哪个没有通信资源。如果您需要对后台任务进行排队而不是仅仅触发它们,那么IntentService可能更合适。