2

我有一个简单的 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 更新回调。这对我来说似乎直截了当、灵活且优越……所以我当然想知道我错过了什么。

当前的框架解决方案如何优越?这种方法是否存在一些重大缺陷?据我所知,代码执行和线程的拓扑在运行时是完全相同的,只是代码耦合更松散(堆栈上还有一些额外的帧)。

4

2 回答 2

2

这与其说是优势问题,不如说是目的和用例。AsyncTask 通常在 Activity 中编写为私有类(或匿名内联声明),因此继承了 Activity 对无论如何都需要更新的各种 UI 元素的引用。

如果 AsyncTask 具有足够的大小和/或复杂性,应该将其拉出到自己的类中,并且可以被其他类重用,那么使用 Handlers 进行更好的解耦是一个好主意。只是它通常不是必需的,因为 AsyncTask 正在完成特定于定义它的 Activity 的某些事情,而对于简单的那些,相应的处理程序代码甚至可能比整个 AsyncTask 本身更大。

于 2012-09-13T18:41:08.053 回答
2

这是在小型项目中分离 UI/Background 任务的优雅解决方案,尽管传递 Runnables 更加优雅。请记住,AsyncTask 是 Thread/Handler 的包装器,因此您正在加倍处理已经在幕后进行的线程消息传递。这里的缺陷是,如果您将 AsyncTask 设计为可重用,则需要确保您正在运行的 IO 都是线程安全的,因为各种 AsyncTask 之间没有关于谁处于活动状态或访问哪个资源。如果您需要将后台任务排队而不是仅仅触发它们,则 IntentService 可能更合适。

于 2012-09-13T18:49:51.947 回答