罗朝辉(飘飘白云)

关注嵌入式操作系统,移动平台,图形开发。-->加微博 ^_^

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  85 随笔 :: 0 文章 :: 169 评论 :: 0 Trackbacks
Android多线程分析之三:Handler,Looper的实现

罗朝辉 (http://www.cppblog.com/kesalin/)

本文遵循“署名-非商业用途-保持一致”创作公用协议

在前文《
Android多线程分析之二:Thread的实现》中已经详细分析了Android Thread 是如何创建,运行以及销毁的,其重点是对相应 native 方法进行分析,今天我将聚焦于 Android Framework 层多线程相关的类:Handler, Looper, MessageQueue, Message 以及它们与Thread 之间的关系。可以用一个不太妥当的比喻来形容它们之间的关联:如果把 Thread 比作生产车间,那么 Looper 就是放在这车间里的生产线,这条生产线源源不断地从 MessageQueue 中获取材料 Messsage,并分发处理 Message (由于Message 通常是完备的,所以 Looper 大多数情况下只是调度让 Message 的 Handler 去处理 Message)。正是因为消息需要在 Looper 中处理,而 Looper 又需运行在 Thread 中,所以不能随随便便在非 UI 线程中进行 UI 操作。 UI 操作通常会通过投递消息来实现,只有往正确的 Looper 投递消息才能得到处理,对于 UI 来说,这个 Looper 一定是运行在 UI 线程中。

在编写 app 的过程中,我们常常会这样来使用 Handler:
Handler mHandler = new Handler();
mHandler.post(new Runnable(){
    @Override
    public void run() {
        // do somework
    }
});

或者如这系列文章第一篇中的示例那样: 
    private Handler mHandler= new Handler(){
        @Override
        public void handleMessage(Message msg) {
            Log.i("UI thread", " >> handleMessage()");

            switch(msg.what){
            case MSG_LOAD_SUCCESS:
                Bitmap bitmap = (Bitmap) msg.obj;
                mImageView.setImageBitmap(bitmap);

                mProgressBar.setProgress(100);
                mProgressBar.setMessage("Image downloading success!");
                mProgressBar.dismiss();
                break;

            case MSG_LOAD_FAILURE:
                mProgressBar.setMessage("Image downloading failure!");
                mProgressBar.dismiss();
                break;
            }
        }
    };

    Message msg = mHandler.obtainMessage(MSG_LOAD_FAILURE, null);
    mHandler.sendMessage(msg);

那么,在 Handler 的 post/sendMessage 背后到底发生了什么事情呢?下面就来解开这个谜团。

首先我们从 Handler 的构造函数开始分析:
    final MessageQueue mQueue; 
    final Looper mLooper; 
    final Callback mCallback; 
    final boolean mAsynchronous;

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler() {
        this(nullfalse);
    }

上面列出了 Handler 的一些成员变量:

mLooper:线程的消息处理循环,注意:并非每一个线程都有消息处理循环,因此 Framework 中线程可以分为两种:有 Looper 的和无 Looper 的。为了方便 app 开发,Framework 提供了一个有 Looper 的 Thread 实现:HandlerThread。在前一篇《Thread的实现》中也提到了两种不同 Thread 的 run() 方法的区别。
/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 
*/
public class HandlerThread extends Thread {
    Looper mLooper;
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     
*/
    protected void onLooperPrepared() {
    }

    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * 
@return The looper.
     
*/
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
}

这个 HandlerThread 与 Thread 相比,多了一个类型为 Looper 成员变量 mLooper,它是在 run() 函数中由 Looper::prepare() 创建的,并保存在 TLS 中:
     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {
@link #loop()} after calling this method, and end it by calling
      * {
@link #quit()}.
      
*/
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

然后通过 Looper::myLooper() 获取创建的 Looper:
    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     
*/
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

最后通过 Looper::Loop() 方法运行消息处理循环:从 MessageQueue 中取出消息,并分发处理该消息,不断地循环这个过程。这个方法将在后面介绍。

Handler 的成员变量 mQueue 是其成员变量 mLooper 的成员变量,这里只是为了简化书写,单独拿出来作为 Handler 的成员变量;成员变量 mCallback 提供了另一种使用Handler 的简便途径:只需实现回调接口 Callback,而无需子类化Handler,下面会讲到的:
    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     
*/
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

成员变量 mAsynchronous 是标识是否异步处理消息,如果是的话,通过该 Handler obtain 得到的消息都被强制设置为异步的。

同是否有无 Looper 来区分 Thread 一样,Handler 的构造函数也分为自带 Looper 和外部 Looper 两大类:如果提供了 Looper,在消息会在该 Looper 中处理,否则消息就会在当前线程的 Looper 中处理,注意这里要确保当前线程一定有 Looper。所有的 UI thread 都是有 Looper 的,因为 view/widget 的实现中大量使用了消息,需要 UI thread 提供 Looper 来处理,可以参考view.java:

view.java

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);
        return true;
    }

ViewRootImpl.java

    private void performTraversals() {
        .
        // Execute enqueued actions on every traversal in case a detached view enqueued an action
        getRunQueue().executeActions(attachInfo.mHandler);
      
    }

    static RunQueue getRunQueue() {
        RunQueue rq = sRunQueues.get();
        if (rq != null) {
            return rq;
        }
        rq = new RunQueue();
        sRunQueues.set(rq);
        return rq;
    }

    /**
     * The run queue is used to enqueue pending work from Views when no Handler is
     * attached.  The work is executed during the next call to performTraversals on
     * the thread.
     * @hide
     
*/
    static final class RunQueue {
    
        void executeActions(Handler handler) {
            synchronized (mActions) {
                final ArrayList<HandlerAction> actions = mActions;
                final int count = actions.size();

                for (int i = 0; i < count; i++) {
                    final HandlerAction handlerAction = actions.get(i);
                    handler.postDelayed(handlerAction.action, handlerAction.delay);
                }

                actions.clear();
            }
        }
    }

从上面的代码可以看出,作为所有控件基类的 view 提供了 post 方法,用于向 UI Thread 发送消息,并在 UI Thread 的 Looper 中处理这些消息,而 UI Thread  一定有 Looper 这是由 ActivityThread 来保证的:
public final class ActivityThread {

    final Looper mLooper = Looper.myLooper();
}

UI 操作需要向 UI 线程发送消息并在其 Looper 中处理这些消息。这就是为什么我们不能在非 UI 线程中更新 UI 的原因,在控件在非 UI 线程中构造 Handler 时,要么由于非 UI 线程没有 Looper 使得获取 myLooper 失败而抛出 RunTimeException,要么即便提供了 Looper,但这个 Looper 并非 UI 线程的 Looper 而不能处理控件消息。为此在 ViewRootImpl 中有一个强制检测 UI 操作是否是在 UI 线程中处理的方法 checkThread():该方法中的 mThread 是在 ViewRootImpl 的构造函数中赋值的,它就是 UI 线程;该方法中的 Thread.currentThread() 是当前进行 UI 操作的线程,如果这个线程不是非 UI 线程就会抛出异常CalledFromWrongThreadException。
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

如果修改《使用Thread异步下载图像》中示例,下载完图像 bitmap 之后,在 Thread 的 run() 函数中设置 ImageView 的图像为该 bitmap,即会抛出上面提到的异常:
W/dalvikvm(796): threadid=11: thread exiting with uncaught exception (group=0x40a71930)
E/AndroidRuntime(796): FATAL EXCEPTION: Thread-75
E/AndroidRuntime(796): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
E/AndroidRuntime(796):     at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4746)
E/AndroidRuntime(796):     at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:823)
E/AndroidRuntime(796):     at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796):     at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796):     at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796):     at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796):     at android.view.View.requestLayout(View.java:15473)
E/AndroidRuntime(796):     at android.widget.ImageView.setImageDrawable(ImageView.java:406)
E/AndroidRuntime(796):     at android.widget.ImageView.setImageBitmap(ImageView.java:421)
E/AndroidRuntime(796):     at com.example.thread01.MainActivity$2$1.run(MainActivity.java:80)

Handler 的构造函数暂且介绍到这里,接下来介绍:handleMessage 和 dispatchMessage:
    /**
     * Subclasses must implement this to receive messages.
     
*/
    public void handleMessage(Message msg) {
    }

    /**
     * Handle system messages here.
     
*/
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

前面提到有两种方式来设置处理消息的代码:一种是设置 Callback 回调,一种是子类化 Handler。而子类化 Handler 其子类就要实现 handleMessage 来处理自定义的消息,如前面的匿名子类示例一样。dispatchMessage 是在 Looper::Loop() 中被调用,即它是在线程的消息处理循环中被调用,这样就能让 Handler 不断地处理各种消息。在 dispatchMessage 的实现中可以看到,如果 Message 有自己的消息处理回调,那么就优先调用消息自己的消息处理回调:
    private static void handleCallback(Message message) {
        message.callback.run();
    }

否则看Handler 是否有消息处理回调 mCallback,如果有且 mCallback 成功处理了这个消息就返回了,否则就调用 handleMessage(通常是子类的实现) 来处理消息。

在分析 Looper::Loop() 这个关键函数之前,先来理一理 Thread,Looper,Handler,MessageQueue 的关系:Thread 需要有 Looper 才能处理消息(也就是说 Looper 是运行在 Thread 中),这是通过在自定义 Thread 的 run() 函数中调用 Looper::prepare() 和 Looper::loop() 来实现,然后在 Looper::loop() 中不断地从 MessageQueue 获取由 Handler 投递到其中的 Message,并调用 Message 的成员变量 Handler 的 dispatchMessage 来处理消息。

下面先来看看 Looper 的构造函数:
    final MessageQueue mQueue;
    final Thread mThread;
    volatile boolean mRun;

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
    }

Looper 的构造函数很简单,创建MessageQueue,保存当前线程到 mThread 中。但它是私有的,只能通过两个静态函数 prepare()/prepareMainLooper() 来调用,前面已经介绍了 prepare(),下面来介绍 prepareMainLooper():
    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {
@link #prepare()}
     
*/
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepareMainLooper 是通过调用 prepare 实现的,不过传入的参数为 false,表示 main Looper 不允许中途被中止,创建之后将looper 保持在静态变量 sMainLooper 中。整个 Framework 框架只有两个地方调用了 prepareMainLooper 方法:

第一处是在 SystemServer.java 中的 ServerThread,ServerThread 的重要性就不用说了,绝大部分 Android Service 都是这个线程中初始化的。这个线程是在 Android 启动过程中的 init2() 方法启动的:
    public static final void init2() {
        Slog.i(TAG, "Entered the Android system server!");
        Thread thr = new ServerThread();
        thr.setName("android.server.ServerThread");
        thr.start();
    }
class ServerThread extends Thread {
    @Override
    public void run() {
        
        Looper.prepareMainLooper();
        
        Looper.loop();
        Slog.d(TAG, "System ServerThread is exiting!");
    }
}

第二处是在 ActivityThread.java 的 main() 方法中:
    public static void main(String[] args) {
        .
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        AsyncTask.init();

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

ActivityThread 的重要性也不言而喻,它是 Activity 的主线程,也就是 UI 线程。注意这里的 AsyncTask.init() ,在后面介绍 AsyncTask 时会详细介绍的,这里只提一下:AsyncTask 能够进行 UI 操作正是由于在这里调用了 init()。

有了前面的铺垫,这下我们就可以来分析 Looper::Loop() 这个关键函数了:
   /**
     * Run the message queue in this thread. Be sure to call
     * {
@link #quit()} to end the loop.
     
*/
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            msg.target.dispatchMessage(msg);

            msg.recycle();
        }
    }

loop() 的实现非常简单,一如前面一再说过的那样:不断地从 MessageQueue 中获取消息,分发消息,回收消息。从上面的代码可以看出,loop() 仅仅是一个不断循环作业的生产流水线,而 MessageQueue 则为它提供原材料 Message,让它去分发处理。至于 Handler 是怎么提交消息到 MessageQueue 中,MessageQueue 又是怎么管理消息的,且待下文分解。
posted on 2014-07-12 11:00 罗朝辉 阅读(2494) 评论(0)  编辑 收藏 引用 所属分类: 移动开发

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理