`
dawuafang
  • 浏览: 1111551 次
文章分类
社区版块
存档分类
最新评论

Android多线程分析之三:Handler,Looper的实现

 
阅读更多

Android多线程分析之三:Handler,Looper的实现

CC 许可,转载请注明出处

在前文《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(null, false);
    }

上面列出了 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 又是怎么管理消息的,且待下文分解。


分享到:
评论

相关推荐

    android 多线程 looper handler

    android 多线程 looper handler的讲解,轻松学明白androd的消息机制,附有很多实例。

    Android中多线程的Handler原理

    Android中多线程的Handler的工作原理,其中涉及到MessageQueue和Looper。详情可以参见博客:http://www.cnblogs.com/plokmju/p/android_Looper.html

    Android_Handler,Looper,Message

    Android ,Handler,Looper,Message

    Android 中三种启用线程的方法总结

    在多线程编程这块,我们经常要使用Handler(处理),Thread(线程)和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢? 首先说明Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而...

    android+多线程机制的讲解和例子

    1.Looper:一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)。 2.Handler:你可以构造Handler对象来与Looper沟通,以便push新消息到Message Queue里;或者接收Looper(从Message Queue...

    Android多线程及异步处理问题详细探讨

    2)多线程实现方式implements Runnable 或 extends Thread 3)多线程核心机制是Handler 4)提供如下几种实现方式 —-1—–Handler ————————————说明1 创建一个Handler时一定要关联一个Looper实例,默认...

    Handler与AsyncTask使用示例

    Handler与AsyncTask使用示例,Handler AsyncTask 示例 looper

    深入Android Handler,MessageQueue与Looper关系

    Handler其实还有很多其他用途,比如我们需要在子线程进行耗时的I/O操作,可能是读取某些文件或者去访问网络等,当耗时操作完成后我们可能需要在UI上做出相应的改变,但由于Android系统的限制,我们是不能在子线程...

    Android后台线程和UI线程通讯实例

    本节向你展示如何在任务中发送数据给UI线程里的对象,这个特性允许你在后台线程工作,完了在UI线程展示结果。...用Handler(Looper)方法实例化,连接到UI线程,构造方法使用Looper对象,也是Android系统线

    android 多线程技术应用

    多线程案例——计时器 这个案例中,屏幕启动之后,进入如图所示的界面。 屏幕上有一个文本框用于显示逝去的时间,此外还有一个“停止... 3)Android中的线程,包括:Message、Handler、Looper和HandlerThread等概念。

    深入理解Android:卷I--详细书签版

     对于Android应用开发工程师而言,本书中关于Binder,以及sp、wp、Handler和Looper等常用类的分析或许能帮助你迅速适应Android平台上的开发工作。  (2)Android系统开发工程师  Android系统开发工程师常常需要...

    swt-async-handler-1.0

    由于,不是很了解android下的Handler机制,没有深层次的编写诸如looper,MessageQueue。 将在下一个版本中加入MessageQueue机制,实现多任务后台处理相应。 具体使用请参考test包下的Handler使用事例。

    Android开发笔记之:消息循环与Looper的详解

    Understanding LooperLooper是用于给一个线程添加一个消息队列(MessageQueue),并且循环等待,当有消息时会唤起线程来处理消息的一个工具,直到线程结束为止。通常情况下不会用到Looper,因为对于Activity,Service...

    Android线程机制_2011_9_30更新

    根据开发经验以及android机制,结合Java多线程和android多线程,说明问题。 给初学android或者对android的多线程机制还有点迷惑的人做点贡献。该文档会持续更新。 推荐:...

    全面总结Android中线程的异步处理方式

    Handler 、 Looper 、Message 这三者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢? 异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,...

    android使用多线程更新ui示例分享

    Android线程涉及的技术有:Handler;Message;MessageQueue;Looper;HandlerThread。 下面看一段在线程中更新UI的代码: 代码如下:public class MainActivity extends Activity {private TextView timeLable;private ...

    android使用handlerthread创建线程示例

    在android开发中,一说起线程的使用,很多人马上想到new Thread(){…}.start()这种方式。这样使用当然可以,但是多次使用这种方式,会创建多个匿名线程。使得程序运行起来越来越慢。因此,可以考虑使用一个Handler来...

    老罗android视频开发源码和ppt经典

    十五、多线程编程 15.1 AsyncTask异步任务介绍一 15.2 AsyTask异步任务介绍二 15.3 Handler和Message(一) 15.4 Handler和Message(二) 15.5 Handler和Message(三) 15.6 Handler和Looper 15.7 Handler综合练习(图文...

    [Android实例] 【版主帖推荐】继人员列表,聊天的实现,包括图片,语音.zip

    [Android实例] 【版主帖推荐】继人员列表,聊天的实现,包括图片,语音. 需要的可以下载使用 对于现阶段的Android开发而言,技术深度很大一部分就体现在对Android系统底层的理解上。只有理解了底层的运作,我们才能...

    [Android实例] Android 竖着的SeekBar.zip

    [Android实例] Android 竖着的SeekBar 需要的请下载 对于现阶段的Android开发而言,技术深度很大一部分就体现在对Android系统底层的理解上。...例如一个线程可以有几个Looper可以对应几个Handler?(字节跳动、小米)

Global site tag (gtag.js) - Google Analytics