我有一个简单的 Android 应用程序,它使用 AsyncTasks 进行 I/O。一个常见的模式:
- 用户单击按钮
- 作为响应,onClick 处理程序实例化并 .execute()s 一个 AsyncTask
- AsyncTask 完成后,应该以某种方式更新 UI
根据 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 编写可重用的 AsyncTask,并通过 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 更新回调。这对我来说似乎直截了当、灵活且优越……所以我当然想知道我错过了什么。
当前的框架解决方案如何优越?这种方法是否存在一些重大缺陷?据我所知,代码执行和线程的拓扑在运行时是完全相同的,只是代码耦合更松散(堆栈上还有一些额外的帧)。