美文网首页Android开发技术干货Android开发
从源码分析Handler,Looper,MessageQueue

从源码分析Handler,Looper,MessageQueue

作者: 孤独的二狗 | 来源:发表于2017-07-24 11:47 被阅读0次

    前言

    首先介绍一下Handler和Looper,MessageQueue分别是做什么的,有什么用!

    • Handler:线程间发送与处理消息
    • Looper:管理MessageQueue消息队列,循环读取MessageQueue中的消息
    • MessageQueue:消息队列
    • Message:消息的载体

    首先我们如果在主线程使用Handler的话,肯定是下面的步骤:

    handler = new Handler(handlerThread.getLooper()){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.d("test","Handler消息!");
                }
    };
    

    为什么没有调用Looper的方法呢?
    因为主线程已经默认给我们调用了Looper的这个两方法

    Looper.prepare();
    Looper.loop();
    

    现在我们开始一步一步的分析

    • 先从Looper的prepare和loop两个方法开始:
      prepare方法:
        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是否为空,如果为空就创建一个新的Looper对象,不为空的话就会抛出异常
    这就是为什么在一个线程Looper不能调用两次preare()方法的原因

    代码中把Looper对象存到了sThreadLocal中,这个sThreadLocal又是什么呢?

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    

    简单分析下ThreadLocal:

    ThreadLocal内部有个ThreadLocalMap类用来存储值的,这个ThreadLocalMap使用键值对进行存储的,在Thread类中有个ThreadLocalMap实例对象,也就是说在每个线程都有一个ThreadLocalMap对象

    ThreadLocal存取值方法

    ThreadLocal.get方法:

    • ThreadLocal会获取当前Thread的ThreadLocalMap对象然后取出值
      TrheadLocal.set方法
    • ThreadLocal会获取当前Thread的ThreadLocalMap对象然后存入值

    假如我们调用Looper的myLooper()方法获取当前Thread的Looper,ThreadLocal就会获取当前Thread来取出当前Thread的ThreadLocalMap对象,然后取出里面的Looper对象

    个人觉得ThreadLocal就是可以为各个线程存储其对应的数据
    

    贴出ThreadLocal部分代码:

        public T get() {
            // 获取当前线程
            Thread t = Thread.currentThread();
            // 取出当前线程的ThreadLocalMap对象
            ThreadLocalMap map = getMap(t);
            // 判断如果map不为空就取出对应的值
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null)
                    return (T)e.value;
            }
            return setInitialValue();
        }
    
        public void set(T value) {
            // 获取当前线程
            Thread t = Thread.currentThread();
            // 取出当前线程的ThreadLocalMap对象
            ThreadLocalMap map = getMap(t);
            // 判断如果map为空则进行创建
            // 不为空就进行存值
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
        // 获取线程中的threadLocals对象
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    

    Ok,现在看loop方法
    我在代码比较重要的地方注释进行解释

        public static void loop() {
            // 获取了当前Thread的Looper对象
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            // 获取当前Looper的MessageQueue
            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(;;){}这种写法就是无限循环的意思
            for (;;) {
                // 获取MessageQueue中的Message对象
                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;
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                // 我们看这里
                // 这里才是关键的地方
                // 调用Message.target对象的dispatchMessage方法,target其实就是Handler的引用;
                // 而Handler的dispatchMessage方法内部又调用了handleMessage方法去处理
                // 而我们每次创建Handler都会实现handlerMessage方法,是不是有点明白什么了.
                // OK这个我们待会详细再将
                try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
    
                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();
            }
        }
    

    现在可以看出来,loop方法内部就是开启了一个死循环,不断的从MessageQueue中取出Message进行处理,
    通过Message.target.dispatchMessage发送Handler所在的线程取处理

    Looper的preare和loop方法分析好了,现在我们开始从sendMessage方法一直往下分析,看下Handler从发送到处理到底经过了哪些流程
    关于Handler发送Message有两种发送方式
    简单说下区别:

            // 1 每次都会新建Message对象
            Message message = new Message();
            message.what = 0;
            handler.sendMessage(message);
    
            // 2 会从Message消息池中取出已存在的Message对象进行复用
            handler.obtainMessage(0).sendToTarget();
    
            // 因为obtainMessage会取出已存在的Message对象进行复用,所以个人建议使用obtainMessage
    

    好的,方便我们的理解,我们就用sendMessage开始分析,
    下面是sendMessage调用过程

        public final boolean sendMessage(Message msg)
        {
            // sendMessage方法延时时间为0,就是不延时处理
            return sendMessageDelayed(msg, 0);
        }
    
        // delayMillis延时时间
        public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            // SystemClock.uptimeMillis()  // 从开机到现在的毫秒数(手机睡眠的时间不包括在内)
            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);
        }
    
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            // 设置当前消息的目标handler
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    

    我们调用sendMessage方法发送消息,sendMessage方法最终会走到enqueueMessage方法中,在enqueueMessage方法中,将message的target对象设置为当前handler对象
    刚才我们在loop方法中是不是看到使用了message.target对象,原来target是在这里赋值的
    然后看方法内部最后调用了queue.equeueMessage方法,也就是说调用了MessageQueue对象的enqueueMessage方法
    我们跟着这个方法进入看看

        boolean enqueueMessage(Message msg, long when) {
            // 如果msg.target为空抛出异常
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            // 如果msg正在处理中抛出异常
            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;
                // 如果p == null || when == 0 || when < p.when,则将此次消息插入消息队列头部
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    // 将msg的下一个位置设置成当前头部的消息
                    msg.next = p;
                    // 再将msg赋值为mMessages
                    // 就完成了当前消息插入消息队列头部的操作
                    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;
        }
    

    关于enqueueMessage内部主要进行消息的插入队列中
    如果p == null || when == 0 || when < p.when则将该条消息插入队列的头部
    (个人理解,p ==null 可能就是第一次插入消息时,when < p.when的时候就是后面插入的消息要比头部的消息先执行,所以插入到头部,先执行意思就是延时时间要比头部的短)
    否则就把消息插入队列的尾部,或者插入延时消息比自己时间要长的前一个位置
    关于插入延时消息比自己时间要长的前一个位置的解释:
    比如现在连续发送两个消息,第一个消息延时1分钟处于消息池中最后一个位置,第二个消息延时3秒的,此时第一个消息会被插入到延时1分钟消息的前面

    Ok此时消息已经被插入消息队列,那么还记得刚才的loop方法吗,因为是无限循环方法,所以被插入到队列的消息会被Looper对象取出并处理
    看代码:

                try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
    

    在loop方法中如果取到message就会调用它的target对象.dispatchMessage方法,刚才已经说了target就是handler对象,现在我们去Handler中的dispatchMessage方法中看看

        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    public void handleMessage(Message msg) {
        }
    
    

    我们先分析msg.callback和mCallback.handleMessage方法,最后再说handleMessage(msg)方法
    关于msg.callback和mCallback.handleMessage方法

    msg.callback是通过Message对象设置的一个Runnable实现对象,当前消息发送之后会出发该实现方法
    关于调用的handleCallback只是竹筏msg.callback的run方法

        private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    关于会走msg.callback的写法

    Message m = Message.obtain(handler, new Runnable() {
                @Override
                public void run() {
    
                }
            });
            m.sendToTarget();
    

    mCallback.handleMessage可以看下面的代码就会明白

     handler = new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message message) {
                    return false;
                }
            });
    

    mCallback是通过构造传入的一个接口,而这个接口中有个方法也是handleMessage(msg)方法
    看代码:
    Handler内部的CallBack接口
    ···
    /**
    * Callback interface you can use when instantiating a Handler to avoid
    * having to implement your own subclass of Handler.
    *
    * @param msg A {@link android.os.Message Message} object
    * @return True if no further handling is desired
    */
    public interface Callback {
    public boolean handleMessage(Message msg);
    }
    ···

    最后说我们重写的handleMessage方法
    如果我们没有使用以上两种方式,而是使用重写handleMessage方法,
    也就是这种:

    handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
    

    那么在dispatchMessage中就会走到else的最后面调用了handlerMessage(msg)方法,而我们又实现了handleMessage方法,
    所以消息会在handleMessage方法中处理掉;

    到此就分析结束了,如有分析错误的地方,欢迎指出,谢谢!
    Paste_Image.png

    相关文章

      网友评论

        本文标题:从源码分析Handler,Looper,MessageQueue

        本文链接:https://www.haomeiwen.com/subject/ijtukxtx.html