在Android中结合使用Handler和AsyncTask - 明显的缺陷?

时间:2012-09-13 18:24:54

标签: java android

我有一个简单的Android应用程序,它使用AsyncTasks进行I / O.一个常见的模式:

  • 用户点击按钮
  • 作为响应,onClick处理程序实例化,而.execute()是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编写可重用的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更新回调。这看起来简单,灵活,优于我......所以当然我想知道我错过了什么。

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

2 个答案:

答案 0 :(得分:2)

这不仅仅是目的和优势的优越性问题。用例。 AsyncTasks通常在活动中编写为私有类(或匿名内联声明),因此继承了Activity对各种需要更新的UI元素的引用。

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

答案 1 :(得分:1)

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