你真的懂Android的Handler机制吗?在回答这个问题之前先问自己几个问题:

1、Handler是如何跟线程绑定的?

2、Handler中的消息是怎么传递的?是通过回调还是通过循环?

3、如果是通过循环传递的,那么为什么消息队列为空时没有引起ANR?是不是在非UI线程中进行的无限循环?

4、如果是在非UI线程中进行的无限循环,那么在UI线程发送消息并且在UI线程中处理消息时是否进行了线程切换?这样做是否浪费了资源?有没有更好的解决方案?

5、Handler是如何进行线程切换的?

我们从Handler对象的创建入手,深挖一下Handler消息机制。

 

上一篇文章我们知道了Handler是如何跟当前线程绑定到一起的,以及ThreadLocal在线程绑定过程中所起的作用,这一篇文章我们继续学习Handler中消息的传递过程。

我们首先熟悉一下Handler的构造方法

/**     * Use the {@link Looper} for the current thread with the specified callback interface     * and set whether the handler should be asynchronous.     *     * Handlers are synchronous by default unless this constructor is used to make     * one that is strictly asynchronous.     *     * Asynchronous messages represent interrupts or events that do not require global ordering     * with respect to synchronous messages.  Asynchronous messages are not subject to     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.     *     * @param callback The callback interface in which to handle messages, or null.     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.     *     * @hide     */    public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> klass = getClass();            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                    (klass.getModifiers() & Modifier.STATIC) == 0) {                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                    klass.getCanonicalName());            }        }        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread " + Thread.currentThread()                        + " that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

通过代码可以看到,在Handler的构造方法中,获取到Looper对象之后将Looper对象的mQueue属性赋值给Handler的mQueue属性,mQueue是MessageQueue类型的属性,用来存放待处理的消息,其原理我们稍后分析。

下面我们开始分析Handler的消息分发过程,我们从Handler的post()方法开始分析。

    /**     * Causes the Runnable r to be added to the message queue.     * The runnable will be run on the thread to which this handler is      * attached.      *       * @param r The Runnable that will be executed.     *      * @return Returns true if the Runnable was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     */    public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }

post()方法会将Runnale参数封装到Message中,然后调用sendMessageDelayed()方法将消息添加到队列中。下面分别是生成message的方法和sendMessageDelayed()方法。

    private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }    /**     * Enqueue a message into the message queue after all pending messages     * before (current time + delayMillis). You will receive it in     * {@link #handleMessage}, in the thread attached to this handler.     *       * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.  Note that a     *         result of true does not mean the message will be processed -- if     *         the looper is quit before the delivery time of the message     *         occurs then the message will be dropped.     */    public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }

sendMessageDelayed()方法只是简单的调用了sendMessageAtTime(),延时0毫秒。

下面是将Message存放到MessageQueue的代码:

    /**     * Enqueue a message into the message queue after all pending messages     * before the absolute time (in milliseconds) uptimeMillis.     * The time-base is {@link android.os.SystemClock#uptimeMillis}.     * Time spent in deep sleep will add an additional delay to execution.     * You will receive it in {@link #handleMessage}, in the thread attached     * to this handler.     *      * @param uptimeMillis The absolute time at which the message should be     *         delivered, using the     *         {@link android.os.SystemClock#uptimeMillis} time-base.     *              * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.  Note that a     *         result of true does not mean the message will be processed -- if     *         the looper is quit before the delivery time of the message     *         occurs then the message will be dropped.     */    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }        return enqueueMessage(queue, msg, uptimeMillis);    }    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

sendMessageAtTime()方法调用了enqueueMessage()。

在enqueueMessage()方法中,首先设置了Message对象的target属性为this,当Handler对象handler处理消息时要先验证消息时应该先验证handler是否等于Message的target,这样就能防止handler处理不需要自己处理的消息;其次设置了Message是否是异步的,默认为false(见Handler的构造方法),异步的消息可以跳过Looper的同步屏障(同步屏障是指,必须要满足一定的条件,该消息才能被处理)。最后enqueueMessage()方法调用了queue.enqueueMessage(msg, uptimeMillis)方法将消息添加到队列中。下面是详细代码:

    boolean enqueueMessage(Message msg, long when) {        if (msg.target == null) {            throw new IllegalArgumentException("Message must have a target.");        }        if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");        }        synchronized (this) {            if (mQuitting) {                IllegalStateException e = new IllegalStateException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w(TAG, e.getMessage(), e);                msg.recycle();                return false;            }            msg.markInUse();            //设置应该处理时间            msg.when = when;            //获取消息队列中的当前消息            Message p = mMessages;            boolean needWake;            //如果消息队列中的当前消息为null,或者应处理时间为0,或者应处理时间小于当前消息的应处理时间,就把新消息插到当前消息前面            if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                // Inserted within the middle of the queue.  Usually we don't have to wake                // up the event queue unless there is a barrier at the head of the queue                // and the message is the earliest asynchronous message in the queue.                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                for (;;) {                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }        }        return true;    }

我们看到queue.enqueueMessage(msg, uptimeMillis)方法将消息按要处理的时间顺序插入到了消息队列中。至此,消息已经发布出去,但是Handler什么时候可以收到消息并处理消息呢?这个时候就要用到Looper了。

我们先看下Handler在线程中的创建过程:

class LooperThread extends Thread {      public Handler mHandler;      public void run() {          //1、调用Looper.prepare(),此时Looper会跟线程绑定          Looper.prepare();          //2、创建Handler对象          mHandler = new Handler() {              public void handleMessage(Message msg) {                  // process incoming messages here              }          };          //3、调用Looper.loop(),这样循环就开始执行          Looper.loop();      }  }

可以看到要在自定义线程中使用Handler,就要初始化Handler对象,这个过程分三步:第一步,调用Looper.prepare()方法,此时会创建MessageQueue消息队列,同时会把当前线程和Looper绑定到一起;第二步,创建Handler对象,这个过程我们在上一篇文章《你真的懂Android Handler吗?(一)》中分析过了;第三步,调用Looper.loop()方法,开启循环机器,这一步是我们要分析的重点。

首先我们追踪一下Looper.prepare()源码:

    /** 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");        }        //todo 注意这里调用了Looper的构造方法        sThreadLocal.set(new Looper(quitAllowed));    }

可以看到在prepare方法中创建了一个Looper对象,并通过调用ThreadLocal的set方法将Looper对象跟当前线程绑定到了一起。

在追一下Loooper的构造方法:

    public final class Looper {        ......                private Looper(boolean quitAllowed) {            //创建MessageQueue对象,这是一个消息队列,用来存储消息            mQueue = new MessageQueue(quitAllowed);            //获取当前线程,用来和Looper绑定            mThread = Thread.currentThread();        }    }

Looper的构造方法是私有的,而且Looper类是final的,也就是不能被继承,所以我们只能在Looper.prepare()方法中调用构造方法。Looper的构造方法中创建了消息队列,同时也获取了当前线程。

下面我们分析最重点的部分,也就是Looper.loop()方法,去探寻一下Handler到底是如何收到消息的。我们还是先看源码:

public static void loop() {        //获取当前线程的looper对象        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        //获取looper对象中的消息队列        final MessageQueue queue = me.mQueue;        // Make sure the identity of this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        // Allow overriding a threshold with a system prop. e.g.        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'        final int thresholdOverride =                SystemProperties.getInt("log.looper."                        + Process.myUid() + "."                        + Thread.currentThread().getName()                        + ".slow", 0);        boolean slowDeliveryDetected = false;        //注意:这里使用了无限循环        for (;;) {            //调用queue.next()方法可能会阻塞            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            final Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            final long traceTag = me.mTraceTag;            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;            if (thresholdOverride > 0) {                slowDispatchThresholdMs = thresholdOverride;                slowDeliveryThresholdMs = thresholdOverride;            }            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);            final boolean needStartTime = logSlowDelivery || logSlowDispatch;            final boolean needEndTime = logSlowDispatch;            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));            }            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;            final long dispatchEnd;            try {                //调用Handler.dispatchMessage()方法处理消息                msg.target.dispatchMessage(msg);                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }            if (logSlowDelivery) {                if (slowDeliveryDetected) {                    if ((dispatchStart - msg.when) <= 10) {                        Slog.w(TAG, "Drained");                        slowDeliveryDetected = false;                    }                } else {                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",                            msg)) {                        // Once we write a slow delivery log, suppress until the queue drains.                        slowDeliveryDetected = true;                    }                }            }            if (logSlowDispatch) {                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);            }            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // Make sure that during the course of dispatching the            // identity of the thread wasn't corrupted.            final long newIdent = Binder.clearCallingIdentity();            if (ident != newIdent) {                Log.wtf(TAG, "Thread identity changed from 0x"                        + Long.toHexString(ident) + " to 0x"                        + Long.toHexString(newIdent) + " while dispatching to "                        + msg.target.getClass().getName() + " "                        + msg.callback + " what=" + msg.what);            }            //进行消息回收            msg.recycleUnchecked();        }    }

上面代码大致包括四个步骤:

1、获取looper中的MessageQueue对象queue;

2、在无限for循环中通过调用queue.next()方法获取一个Message对象msg,注意这个过程可能会阻塞线程;

3、调用msg.target.dispatchMessage(msg)将消息传递给Handler,上一篇文章介绍过,msg.target就是要处理消息的Handler。

4、最后调用msg.recycleUnchecked()方法回收消息。

由此我们解答了第二,第四和第五个问题,handler中的消息是在当前线程通过无限for循环传递的,而不是单独开启一个UI线程执行循环操作,所以也就不涉及线程切换。那么为什么在主线程中无限for循环没有引起ANR呢?我们需要去MessageQueue.next()方法中寻找答案。这个问题我们先放一放,下一篇文章我们将继续探索这个问题。

现在我们要看一看Handler是怎么处理消息的:

    /**     * 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);        }    }    private static void handleCallback(Message message) {        message.callback.run();    }    /**     * Subclasses must implement this to receive messages.     */    public void handleMessage(Message msg) {    }

因为在Looper.loop()方法中会调用Handler的dispatchMessage()去分发消息,所以上面我们就贴上了这段代码。可以看到如果Message中有CallBack,Handler就会直接调用message.callback.run()方法去处理消息,比如下面这种情况:

new Thread(new Runnable{    @Override    public void run() {        //在自线程中发送消息        handler.post(new Runnable {            ....        });    }}).start();

此时Post中的Runnable会被赋值给message中的callback,所以如果要以这种方式发送消息,那么在主线程中创建Handler对象时就不需要实现handleMessage()方法,也不需要传入Runnable对象,因为它们不会被调用。

如果Message的callback为null,那么就需要判断Handler中的mCallback对象是否为空,如果不为空而且mCallback会处理了消息(也就是mCallback.handleMessage()方法返回true),消息就不会传到Handler的的handleMessage()方法中了,否则又Handler的handleMessage()方法处理消息,下面我们举个例子:

class MainActivity : AppCompatActivity() {    val mHandler: MyOwnHandler = MyOwnHandler(object : Handler.Callback {        override fun handleMessage(msg: Message?): Boolean {            if (msg?.what == 1) {                Log.i("MyOwnHandler", "Handler.Callback.handleMessage")                return true            }            return false        }    })    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        handler_btn_1.setOnClickListener {            Thread(Runnable { mHandler.sendEmptyMessage(1) }).start()        }        handler_btn_2.setOnClickListener {            Thread(Runnable { mHandler.sendEmptyMessage(2) }).start()        }    }    class MyOwnHandler(callback: Callback) : Handler(callback) {        private val TAG = "MyOwnHandler"        override fun handleMessage(msg: Message?) {            super.handleMessage(msg)            if (msg?.what == 2) {                Log.i(TAG, "Handler.handleMessage")            }        }    }}

上面是个MainActivity,MainActivity中有两个按钮handler_btn_1和handler_btn_2,点击handler_btn_1会发送消息1,点击handler_btn_2会发送消息2。于此同时,我们通过继承Handler,定义了自己的MyOwnHandler,在这个Handler中用Callback去处理消息1,用Handler的handleMessage()方法处理消息2。

下面分别是点击handler_btn_1和点击handler_btn_2打出的log:

Callback.handleMessage()处理了消息1,Handler.handleMessage()处理了消息2。

至此,Handler的消息机制我们大体学完了,消息传递的时序图如下:

通过时序图可以看到,发送消息和处理消息的其实是同一个Handler对象。这就引出一个问题,我们在Activity或Fragment中创建的Handler对象如果传给了一个耗时的线程,就有可能会导致内存泄露,因为Handler是Activity或Fragment对象的属性,如果Activity或Fragment对象被回收时,其handler属性还在被长时间运行的线程持有,那么Activity或Fragment对象就无法被回收,就会出现内存泄露,所以我们应该尽可能避免这种情况,如果实在避免不了,可以将自线程中的对Handler的引用改为弱引用,或者将Handler对象改为static的。

最后是Handler消息机制的类图,可以帮我我们梳理这部分代码:

更多相关文章

  1. mac下Android(安卓)Studio常用的一些快捷键
  2. Android用Broadcast实现EventBus的功能和用法
  3. Android(安卓)--- App列表之分组ListView
  4. android Activity状态以及生命周期
  5. 仿微信、短信、QQ等消息数目右上角红色小圆球气泡显示(基于Androi
  6. 老生常谈Android(安卓)HapticFeedback(震动反馈)
  7. Android(安卓)多线程及多线程中更新控件。
  8. Android(安卓)UI事件处理
  9. Android中的进程间通信(IPC机制)

随机推荐

  1. Android跨进程通信IPC之1——Linux基础
  2. Android(安卓)spinner 样式及其使用详解
  3. Android跨进程通信IPC之11——AIDL
  4. Android锁屏的问题
  5. 编译 Linux 3.5 内核烧写 Android(安卓)4
  6. Android主题设置为@android:style/Theme.
  7. TextView跑马灯效果
  8. Android跨进程通信IPC之2——Bionic
  9. Android(Lollipop/5.0) Material Design(
  10. android绘图看这篇就够了