代码改变世界

Why UI not thread-safe?

2010-12-15 15:39  RayLee  阅读(643)  评论(0编辑  收藏  举报

Android开发中,初学者很容易犯的错误就是在非UI线程中直接更新UI控件,Android将抛出异常。查询SDK文档,给出的解释是“UI is not thread-safe”,只能在UI线程中更新控件。

那你是否明白UI为什么设计成非线程安全?

下面这段摘自“Java Swing”一书,给了一个解释,或许也适合Android UI的设计思想。

As we mentioned above, a Swing component draws itself based on the state values in its model. However, if the state values change while the component is in the process of repainting, the component can repaint incorrectly—this is unacceptable. To compound matters, placing a lock on the entire model, as well as on some of the critical component data, or even cloning the data in question, could seriously hamper performance for each refresh. The only feasible solution, therefore, is to place state changes in serial with refreshes. This ensures that modifications in component state do not occur at the same time that Swing is repainting any components, and no race conditions will occur.

你发现最终的决定因素是保持正确性的同时,达到性能要求。

那么,Android平台提供了哪些工具在非UI线程中更新UI?

Handler

SDK文档对Handler的解释:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

我们最常见的用途是情形(2),在UI线程中创建一个Handler对象,利用该对象更新UI。当然,它并不局限于此,它是线程间通信的一种通用方式。

runOnUiThread (Runnable action)

通过Activity的runOnUiThread()方法,也可以实现在非UI线程中更新UI。查看源码,该方法的实现如下:

/**
     * Runs the specified action on the UI thread. If the current thread is the UI
     * thread, then the action is executed immediately. If the current thread is
     * not the UI thread, the action is posted to the event queue of the UI thread.
     *
     * @param action the action to run on the UI thread
     */
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

其实,该方法就是对Handler的一种应用,它特定的应用于UI线程。从开发者角度来看,它是一种封装,提供一种更便捷的访问方式。

AsyncTask

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask是Android提供的另一种抽象,它封装了线程及Handler的一些操作。开发者只需关心在后台线程中要完成什么任务,以及UI线程如何应用任务返回的结果。另外值得一提的是,AsyncTask使用了包java.util.concurrent.*,而不是简单的Thread。

总结

综上所述,第一,UI的非线程安全是由性能造成的,UI的访问是序列化的(通过消息机制)。第二,Android平台最核心的是Handler,runOnUiThread()和AsyncTask是对Handler更高一个层次的抽象。