Handler消息处理机制总结

477 阅读7分钟

参考

Handler消息机制

详解 Handler 消息处理机制

hanlder 使用


        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 1){
                    //
                }
            }
        };
        Message message = Message.obtain();
        message.what = 1;
        message.arg1 = 1;
        //1
        handler.sendMessage(message);
        //或者2
        handler.sendMessageDelayed(message,1000);
        //或者3
        handler.post(new Runnable() {
            @Override
            public void run() {

            }
        });

接下来开始从源码开始分析,先看 Handler 的构造方法

Handler 源码分析


 public Handler(Callback callback, boolean async) {
         .....
        mLooper = Looper.myLooper();
        if (mLooper == null) { // 如果没有手动调用 Looper.prepare,则报错;主线程中默认有一个 Looper,子线程需要手动调用
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

看下主线程在哪里初始 Looper,看到应用的入口 ActivityThread # main

初始化 Looper


public static void main(String[] args) {
        Looper.prepareMainLooper();   // 初始化 Looper
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        Looper.loop(); // 开始消息循环
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

接着分析, Looper.prepareMainLooper()


 public static void prepareMainLooper() {
        prepare(false);   // 初始化,参数 false 表示不允许退出
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();  // 从 sThreadLocal.get()  获取 looper
        }
    }

.....

 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 并放入 ThreaLocal 中
    }

....

 private Looper(boolean quitAllowed) { 
        mQueue = new MessageQueue(quitAllowed);   // 创建消息队列
        mThread = Thread.currentThread(); // 当前线程
    }

上面看到通过 prepare() 创建了 Looper,并把 Looper 放入ThreadLocal中。

接下来回到 ActivityThread # main,接着看 Looper.loop()

轮询消息队列


public static void loop() {
        final Looper me = myLooper();  // 从 ThreadLocl 中获取一个 Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue; // 从 Looper 中拿出消息队列

      ....
        for (;;) { // 死循环
            Message msg = queue.next(); // 从消息队列中,拿出消息
            if (msg == null) {
                return;
            }
       .....
            try {
                msg.target.dispatchMessage(msg); // 获取到消息,分发处理
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            msg.recycleUnchecked();  // 回收处理完成的 Message,可以通过Message.obtain()复用
        }
    }

loop() 主要是从 ThreadLocal 里拿出当前线程的 Looper,然后又从 Looper 里面拿出消息队列 MessageQueue,不断轮询消息队列,如果有消息,则分发处理

接下来,我们看如何从消息队列中,拿出消息:queue.next()

从队列取出消息


 Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {   // if the message loop has already quit and been disposed
            return null;
        }
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(ptr, nextPollTimeoutMillis); // native 方法,应该是唤醒线程的
            synchronized (this) {
                // Try to retrieve the next message. Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) { // target ==null,
                    do {
                        prevMsg = msg;
                        msg = msg.next; // 取出下一条消息
                    } while (msg != null && !msg.isAsynchronous()); // 查找异步的信息
                }
                if (msg != null) {
                    if (now < msg.when) {
                     // 延时处理,计算定时时的唤醒时间 ----下一个消息,将要阻塞的时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                      // 获取到一条消息,因为消息队列是链式结构,所以需要调整一下链表
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else { // 没有消息
                    nextPollTimeoutMillis = -1;
                }
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
             .....

               if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

             } // --- synchronized(this)  end
            
            for (int i = 0; i < pendingIdleHandlerCount; i++) {

              // idleHandle 是消息队列空闲时才会执行的 Handler 。应用: leakcanary中检测内存泄漏的耗时任务会等到主线程空闲才执行
                final IdleHandler idler = mPendingIdleHandlers[i];  
                mPendingIdleHandlers[i] = null; // release the reference to the handler
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
           ......
        }
    }

next(),主要任务是取出一条消息任务,如果消息任务有延时,会计算一下多久后执行,然后再下一次循环中,调用 native 方法进行一个类型闹钟的设置,时间到了会唤醒 next 方法。因为消息列表是单链表结构,所以取出一条消息后,链表需要移动。

处理消息

从消息队列 MessageQueue 拿到了下一条消息,回到 Looper.loop() 中通过 msg.target.dispatchMessage(msg) 处理消息,看到调用了message 的target,而这个 target 就是一个 Hanlder 对象,什么时候传进来的呢,稍后再看 。因此 loop 消息分发处理,我们回到了 Handler # dispatchMessage 中


public void dispatchMessage(Message msg) {
        if (msg.callback != null) {

      // 调用Message中 callback(其实就是一个 Runnable)
     // callback的传入是在调用 Message.obtain(Handler h, Runnable callback) 时
            handleCallback(msg); 
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {  // mCallback是在创建 Handler 的时使用 Handler(Callback callback) 传入的
                    return;
                }
            }
            handleMessage(msg); // 一般我们重写 Hanlder 的 handleMessage 方法
        }
    }

消息入队

上面分析了 Looper 初始化、Looper 轮询消息队列、取出消息队列并处理,还差一个消息添加到消息队列中。

在 Hanlder 中,看到很多添加消息的方法,但最终还是走了enqueueMessage()


 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; // message 的 target 就是当前的 handler 对象,msg.target.dispatchMessage(msg) 中的 target
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis); // 放入MessageQueue 
    }

接下来,看如何放入队列的 MessageQueue # enqueueMessage


 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        synchronized (this) {
            if (mQuitting) {
                msg.recycle();
                return false;
            }
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;  //列表头部
            boolean needWake;

            // 入队规则:队列没有消息、入队消息没有延时、入队消息的延时比队列头的延时早
            if (p == null || when == 0 || when < p.when) {  // 插入链表头部, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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); // 如果需要,调用 native 方法唤醒
            }
        }
        return true;
    }

希望分析完后,可以把下面的问题都能弄得明白

FAQ

Q: 说一下 hanlder 机制

从 Looper 初始化、消息循环、取出消息、处理消息、消息入队这几方面来说,如下:

a. 在应用启动的时候,也就是ActivityThread的main方法里面,创建了Looper和MessageQueue,然后调用Looper.loop 开启消息循环

b. 消息循环是这样的,调用 MessageQueue 的 next 方法,循环从消息队列中取出一条消息,然后交给Handler去处理,一般是回调handleMessage方法,取不到消息就阻塞,直到下一个消息入队或者其它延时消息时间到了就唤醒消息队列。

c. 消息入队,通过调用 handler 的 sendMessage 方法,内部是调用 MessageQueue 的 enqueueMessage 方法,进行消息入队。入队的规制是:队列没有消息,或者要入队的消息没有设置delay,或者delay时间比队列头的消息delay时间短,则将要入队的消息放到队列头,否则就插到队列中间,需要移动链表。

Q:ThreadLocal 是什么?

Threadlocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后只能在指定线程中可以获取到存储的数据,对于其他的线程来说则无法获取到数据

Q:延时消息时如何处理的?

队列中没有消息,插入队列头;

队列不为空,则与队列头的消息比较一下延时,如果插入消息延时短,则插入消息头;

从规则看到,延时消息不是先延迟在发送,也是直接发送消息,而在 MessageQueue.next() 中,如果拿到一个延时消息,通过 Native 方法阻塞线程一定时间,等到消息的执行时间到后再取出消息执行

Q:那消息入队规则有什么好处呢?

延时越长的消息在队列越后面,因此消息循环时,next方法拿到一个延时消息时,如果判断时间没有到,则阻塞队列,不用管后面的消息。

Q:post、sendMessage 发送消息有什么区别


mHandler.sendMessage(message);
mHandler.post(new Runnable() {
          @Override
          public void run() {
            // todo
          }
        });

post 一类的方法发送的是 Runnable 对象,但是其最后还是会被封装成 Message 对象,将 Runnable 对象赋值给 Message 对象中的 callback 变量,然后交由 sendMessageAtTime() 方法发送出去。
在处理消息时,会在 dispatchMessage() 方法里首先被 handleCallback(msg) 方法执行,实际上就是执行 Message 对象里面的 Runnable 对象的 run 方法。

而 sendMessage 一类的方法发送的直接是 Message 对象,处理消息时,在 dispatchMessage 里优先级会低于 handleCallback(msg) 方法,是通过自己重写的 handleMessage(msg) 方法执行。