Android handler 消息机制源码解读

用过 okhttp 网络请求框架的人都知道,okhttp 的 callback 接口不在主线程。因此要想在 callback 中进行 UI上更新需要切换到主线程,可以handler.post(runable),那么,handler 是怎么直接就切换到主线程的呢?handler 是怎么处理线程之间的消息传递呢?

本文主要解决的几个问题

  1. handler 是怎样传递消息的?
  2. handler.post(runable) 方法是怎么直接切换到主线程的?
  3. Handler,Looper,MessageQueue,Message 有什么关系?

相关类介绍

Message 消息对象

    public int what;

    public int arg1; 

    public int arg2;

    public Object obj;

    public Messenger replyTo;

    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;

    private static boolean gCheckRecycle = true;

这是Message的一些属性,创建一个消息实例一般有两种:

// 第一种,直接新建一个对象
Message msg = new Message();
// 第二种,通过Message 的静态方法获取一个对象
Message msg = Message.obtain();
Message msg = Message.obtain((Message orig);
Message msg = Message.obtain(Handler h);
Message msg = Message.obtain(Handler h, Runnable callback);
Message msg = Message.obtain(Handler h, int what);
Message msg = Message.obtain(Handler h, int what, Object obj);
Message msg = Message.obtain(Handler h, int what, int arg1, int arg2);
Message msg = Message.obtain(Handler h, int what,  int arg1, int arg2, Object obj);

这里推荐用第二种。那么两种方式有什么不一样吗?Message.obtain(…)方法最终都是调用的是Message.obtain(),所以我们看一下 obtain() 的源码。

	/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

先看注释:从全局池中返回一个新的消息对象,允许我们在很多情况下避免分配新的对象。其实就是复用以前用过的对象,亦可以看出Message是一个链表结构,如果 sPool 为空就新建一个,反之就从链表中取。注意到 sPoolSize 减一,那么最多可以获取多少个Message实例呢?Message 有一个属性 MAX_POOL_SIZE ,值为50,这个属性中在回收Message时被用到。

    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    void recycleUnchecked() {
       ...
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

如果此时消息池中的可用message数量小于MAX_POOL_SIZE,则该message加入链表尾部,同时可用数量加一,否则舍弃。所以最多可获取50个Message实例。

Handler 消息处理类

消息实体类有了,发送消息就简单了,大概分为两种,一种handler.postXXX(Runable, xxx),一种handler.sendMessageXXX(message);

	// 一种 post
	public final boolean post(Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean postAtTime(Runnable r, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

    public final boolean postDelayed(Runnable r, long delayMillis){
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    
    public final boolean postAtFrontOfQueue(Runnable r) {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

	// 一种sendMessage
	public final boolean sendMessage(Message msg)  {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendEmptyMessage(int what){
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    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);
    }

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }


其实调到最后也是一种而已,因为 post 类最后也是把参数转为Message,然后调用sendMessage()方法的,看源码由 getPostMessage(Runable)这个方法。

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

而 sendMessageXXX()方法最后会调用 enqueueMessage 方法,所以我们重点看一下这个方法。

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看出 msg.target 是个 handler 类型的对象,把当前 handler 赋值给它。最后调用 queue.enqueueMessage 方法。quene 是一个MessageQueue 类型的对象。MessageQueue,顾名思义就是消息队列,我们通过 handler 发送的消息都会被放到这样的一个队列里面。我们继续看 enqueueMessage 的源码。

    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;
            // 如果这是第一个消息,或者这个消息需要马上执行,或者这个消息要比之前的消息先执行
            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;
    }

前面做一些状态判断,不符合就抛出异常。后面把 mMessages 赋值给 p。前面我们说过,Message 其实是一个链表结构。这里后面的两个if - else 里面就是把发来的消息按延迟的时间排序。 if 里面的条件 (p == null || when == 0 || when < p.when),如果这是第一个消息,或者这个消息需要马上执行,或者发来的消息要比队列里第一个消息先执行,那么把这个消息放在链表的第一个位置(原来是msg ,p,变成 msg —> p)。else 里面有一个循环,先把 p 的头部赋值给 prev ,然后 p.next 赋值给 p ,如果 p 为空或者当前消息比 p 先执行,那么就跳出循环。此时的 p 是执行时间比 msg 要晚,而prev 又比当前 msg 执行时间早。然后做一个链表的插入操作。插入前顺序为prev---->p,插入后顺序为 prev ----> msg ----> p。
发来的消息顺序也排好了,那么什么时候去处理消息呢?我们使用 handler 的时候一般会这样使用:

	private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                Toast.makeText(MainActivity.this, "刷新UI", Toast.LENGTH_SHORT).show();
            }
        }
    };

重写 handlerMessage 方法,去实现自己的操作。那么什么时候调用的这个方法呢?从 handler 类中有一个dispatchMessage的方法。

    /**
     * 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);
        }
    }

这个方法里面最后调用了 handlerMessage 方法。又是谁调用这个方法呢?handler 类中并没有什么地方调用,一顿搜索之后,发现是 Looper 里面的 loop() 方法调用了该方法。但是我们平时使用handler 的时候并没有接触到这个类,是什么地方使用的呢?这里就要看 handler 的构造函数了。

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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 that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
     public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

大概分为两类,参数传 looper 和不传 looper,最后都是得到一个 Looper 实例。不传 looper 的最后会调用 Looper.myLooper() 去得到一个 looper,同时也得到一个 MessageQueue 。如果 looper 为空就会抛出异常,看这个异常描述是不是很熟悉?如果你在子线程中直接创建一个 handler 就会抛这个异常。为什么在主线程中不会报异常呢?因为主线程启动的时候,系统已经帮我们调用了这个方法,所以就不会报错了。接下来我们看一下 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;

        // 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();

        for (;;) {
            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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            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();
        }
    }

先看注释:在当前线程运行消息队列,确保使用{@link #quit()} 结束循环。所以如果我们是在主线程中实例化 handler,那么这个消息队列就运行在主线程中了,那么用 handler.post() 就可以直接操作 UI了。

总结

总结一下

  1. 在创建 handler 的时候,会在 handler 所在的线程中创建一个Looper 对象,同时Looper 也会创建一个 MessageQueue 对象。通过 Looper.loop() 开启一个死循环,从MessageQueue 中不断取出 Message,然后通过 handler 将消息分发传回handler所在的线程。
  2. MessageQueue 名字叫做消息队列,实质上是一个链表结构,传来的消息都按照执行时间进行了排序。

另外,关于 handler 还有很多需要我们注意的地方,具体内容参考下一篇,handler 消息机制需要注意的地方有哪些?

你可能感兴趣的:(android)