美文网首页
Handler源码分析

Handler源码分析

作者: yqianqiao | 来源:发表于2020-05-19 17:37 被阅读0次

    1.前言

    Handler不管是作为一种消息机制,还是作为切换线程的手段,在Android中都有充足的应用场景,因为一个APP的运行过程,是不断接受消息以及处理消息的过程。比如Activity,从启动、创建、生命周期回调、销毁,都是借由Handler发送消息来驱动完成。从一个APK的安装,到一个View的更新,都离不开Handler的帮助。

    2.Handler用法

    class MainActivity : AppCompatActivity() {
        //创建handler实例,并实现构造方法中接口Handler.Callback
        private val handler = Handler(Handler.Callback {
            //打印出当前线程名字以及数据arg1
            Log.e(
                "TAG",
                "handleMessage -> ThreadName : ${Thread.currentThread().name} + Handler.arg1 : ${it.arg1}"
            )
            false
        })
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            //创建一个子线程
            Thread {
                Log.e("TAG", "ThreadName : ${Thread.currentThread().name}")
                //创建一个message
                val message = Message.obtain()
                //给message的arg赋值
                message.arg1 = 3
                //通过handler发送消息message
                handler.sendMessage(message)
            }.start()
    
        }
    }
    
    运行结果

    3.Handler源码分析

    handler.png
    • ActivityThread
      APP进程的初始类,它的main函数是APP进程的入口,代表了主线程,但不是一个Thread类,它是主线程操作的管理者,与AMS交互并管理ActivityService
    public final class ActivityThread extends ClientTransactionHandler {
          ...
          // 仅贴出关键源码
         public static void main(String[] args) {
            ... // 仅贴出关键代码
            //1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
            //注: 最终调用prepare() 中 sThreadLocal.set(new Looper(quitAllowed)); 创建Looper对象
            Looper.prepareMainLooper();
            //2.创建主线程
            ActivityThread thread = new ActivityThread();
            //自动开启消息循环
            Looper.loop();
        }
    }
    

    这就解释了为什么主线程在使用handler的时候不需要Loopre.prepare()Looper.loop()了,而子线程需要?这是因为在应用启动的时候ActivityThread已经调用过了。

    • ThreadLocal
      从名字我们就可以看到ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。在这里可以简单的理解为它内部维护了一个map<Thread,Looper>key为当前线程,value为当前线程绑定的Loop对象
    public class ThreadLocal<T> {
        
        public T get() {
            //获取当前线程
            Thread t = Thread.currentThread();
            //获取以线程为key的map
            ThreadLocalMap map = getMap(t);
            //判断map是否为空
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            //若为空,则初始化value
            return setInitialValue();
        }
    
         public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
        private T setInitialValue() {
            //initialValue() 默认值返回null
            T value = initialValue();
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                // t.threadLocals = new ThreadLocalMap(this, firstValue);
                //创建一个ThreadLocalMap
                createMap(t, value);
            //返回默认值null
            return value;
        }
    }
    
    • Looper
    public final class Looper {
     //构造方法 quitAllowed为标志,true:子线程  false:主线程
    private Looper(boolean quitAllowed) {
          // 1. 创建1个消息队列对象(MessageQueue)
            // 即 当创建1个Looper实例时,会自动创建一个与之配对的消息(MessageQueue)
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    
        //主线程调用
        public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                //主线程的Looper对象
                sMainLooper = myLooper();
            }
        }
        //子线程调用
        public static void prepare() {
            prepare(true);
        }
        //sThreadLocal 赋值 子线程和主线程最终都调用到此处(必须调用)
        private static void prepare(boolean quitAllowed) {
           // 1. 判断sThreadLocal是否为null,否则抛出异常
            //即 Looper.prepare()方法不能被调用两次 = 1个线程中只能对应1个Looper实例
            // 注:sThreadLocal = 1个ThreadLocal对象,用于存储线程的变量
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
             // 2. 若为初次Looper.prepare(),则创建Looper对象 & 存放在ThreadLocal变量中
            // 注:Looper对象是存放在Thread线程里的
            // 源码分析Looper的构造方法
            //quitAllowed为标志,true:子线程  false:主线程
            sThreadLocal.set(new Looper(quitAllowed));
        }
        //获取当前线程的Looper对象
         public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    
        //开启循环器(必须调用)
        public static void loop() {
             ...// 仅贴出关键代码
            //1.获取当前线程的Looper
            final Looper me = myLooper();
              // myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
              // 即loop()执行前必须执行prepare(),从而创建1个Looper实例
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            // 获取Looper实例中的消息队列对象(MessageQueue)
            final MessageQueue queue = me.mQueue;
            //2. 消息循环(通过for循环)
            //这里会有个小疑问,同样是死循环,为什么不用while () 而使用 for (;;) 
            // while(true) 编译后    mov     eax,1        for (;;)编译后   jmp     wmain+29h  
            //                      test    eax,eax 
            //                      je      wmain+29h 
            //                      jmp     wmain+1Eh  
            //由上面的结果可以看出for编译器会优化成一条汇编指令,而while编译器会有很多条汇编指令
            // 结果:for ( ; ; )指令少,不占用寄存器,而且没有判断、跳转 
            for (;;) {
                //2.1 从消息队列中取出消息,下面messageQueue马上分析
                 // next():取出消息队列里的消息
                // 若取出的消息为空,则线程阻塞
                Message msg = queue.next(); // might block
                // 2.2 派发消息到对应的Handler
                // 把消息Message派发给消息对象msg的target属性
                // target属性实际是1个handler对象
               msg.target.dispatchMessage(msg);
                // 3. 释放消息占据的资源
               msg.recycleUnchecked();
            }
        }
    }
    
    • MessageQueue
    //单链表的消息队列
    public final class MessageQueue {
          
            //出队消息,即从 消息队列中 移出该消息
          Message next() {
            ...// 仅贴出关键代码
             // 该参数用于确定消息队列中是否还有消息
            // 从而决定消息队列应处于出队消息状态 or 等待状态
            int nextPollTimeoutMillis = 0;
            for (;;) {
               //nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态。
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    // 获取当前时间
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    //是否开启异步处理消息的(优先处理)
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    // 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
                    if (msg != null) {
                         //判断当前时间与消息的时间比较
                        if (now < msg.when) {  
                            // 计算距离下个消息所需时间
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // 开始取消息
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                             //链表删除
                            msg.next = null;
                            msg.markInUse();
                            //返回消息
                            return msg;
                        }
                    } else {
                        // 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
                        // 下次循环时,消息队列则处于等待状态
                        nextPollTimeoutMillis = -1;
                    }
                }
            }
        }
    
        //作用:入队,即 将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
        //采用单链表实现:提高插入消息、删除消息的效率
        boolean enqueueMessage(Message msg, long when) {
              ...// 仅贴出关键代码
            synchronized (this) {
                msg.when = when;
                //获得链表头
                Message p = mMessages;
                boolean needWake;
                // 判断消息队列里有无消息
               // a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
                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;
                    // b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
                    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;
        }
    }
    

    总结下流程:

    1. 当我们启动应用时最先启动的是ActivityThread中的main()方法。
    2. main()里面调用Looper.prepareMainLooper()标识了主线程quitAllowed,最终调用prepare()方法的sThreadLocal.set(new Looper(quitAllowed))。解释一下,sThreadLocal里面有个mapkey为线程,valuelooper,一个线程对应唯一个Looper。在此处创建并绑定当前线程的Looper
    3. Looper 构造方法为私有的,并且里面创建了一个MessageQueue对象,所以Looper也是唯一绑定在Looper里面的。
    4. 1,2,3小结:创建一个Map<Therad,Looper>,并设置数据创建Looper,通过Looper构造方法绑定MessageQueue
    5. main()最后还调用了Looper.loop()启动循环器,通过myLooper()获取当前线程MessageQueue消息队列,在死循环里不断调用queue.next()获取消息,最后通过msg.target.dispatchMessage(msg)分发消息到handler中。
    6. MessageQueue.next()通过死循环不断的从链表中读取消息,没有消息时则通过nativePollOnce方法来阻塞线程。
    7. 5,6小结:通过调用Looper.loop()方法不断从MessageQueue.next()里读取消息,然后通过handler.dispatchMessage(Message msg)把消息分发出去。
    8. 至此除了发送消息之外的流程都已经走完,剩下的就是如何通过handler把消息发送到消息队列里面去了。
    • Message
    public final class Message implements Parcelable {
        //Message复用池最多缓存50个
         private static final int MAX_POOL_SIZE = 50;
    
          // Message内部维护了1个Message池,用于Message消息对象的复用
          // 使用obtain()则是直接从池内获取
          public static Message obtain() {
            synchronized (sPoolSync) {
                //判段有没有回收的message
                if (sPool != null) {
                    Message m = sPool;
                    //链表取出
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
              // 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存
            }
            // 若池内无消息对象可复用,则还是用关键字new创建
            return new Message();
        }
    }
    
    • Handler
    //创建handler方式有两种,一个是匿名内部类,一个是构造方法,两者有什么区别,怎么选择?
     private val handler = Handler(Handler.Callback {
            false
        })
     private val handler1 =object :Handler(){
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
            }
        }
    public class Handler {
         public Handler() {
            this(null, false);
        }
    public Handler(@Nullable Callback callback, boolean async) {
            ...//仅贴关键代码
            //获取ThreadLocal线程中的Looper对象
            mLooper = Looper.myLooper();
            //如果子线程未调用Looper.prepare()则抛异常
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            //绑定消息队列对象(MessageQueue)
            mQueue = mLooper.mQueue;
            //将构造方法的callback绑定到mCallback 
            //下面分发消息判断会用到
            mCallback = callback;
        }
        //构造方法的接口Callback 
        public interface Callback {
              boolean handleMessage(@NonNull Message msg);
        }
       // 注:该方法 = 空方法,在创建Handler实例时复写 = 自定义消息处理方式
        public void handleMessage(@NonNull Message msg) {
        }
        
         //最终会被looper.loop 调用  msg.target.dispatchMessage(msg)回调到这方法
        //派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
        public void dispatchMessage(@NonNull Message msg) {
              // 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
              // 则执行handleCallback(msg),即回调Runnable对象里复写的run()
              // 上述结论会在讲解使用“post(Runnable r)”方式时讲解
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                //判断msg.callback属性是否为空
                if (mCallback != null) {
                     //若msg.callback属性不为空,则调用构造方法中的匿名内部类
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
               // 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息(即此处需讨论的)
                // 则执行handleMessage(msg),即回调复写的handleMessage(msg) 
                handleMessage(msg);
            }
        }
      //结论:两个种创建handler方法最终都会调用到handleMessage。
      //先判断构造方法接口模式,当没有实现接口方式才会去调用重写方式,官方是推荐使用接口模式的。
    
      // --------------------------------------------- 发送消息------------------------------------------------------
        //发送消息 sendMessage()最终会调用到此方法
        public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            //设置消息时间最少为当前时间
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
      //设置发送时间的消息
        public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            //构造方法通过mLooper.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(@NonNull MessageQueue queue, @NonNull Message msg,
                long uptimeMillis) {
            // 1. 将msg.target赋值为this
            // 即 :把 当前的Handler实例对象作为msg的target属性
            // 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
            // 实际上则是将该消息派发给对应的Handler实例 
            msg.target = this;
            // 2. 调用消息队列的enqueueMessage()
            // 即:Handler发送的消息,最终是保存到消息队列,详情看上面MessageQueue中的enqueueMessage分析
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    }
    

    总结:
    创建handler并实现构造方法Callback接口,在子线程中调用handler.sendMessage(),最终调用到queue.enqueueMessage中根据时间排序,把消息Message存入消息队列MessageQueue中,然后等待Looper.loop()queue.next()取出消息,通过Message中的target就是实例化handler调用构造方法CallbackhandleMessage方法完成消息传递。

    总结

    相关文章

      网友评论

          本文标题:Handler源码分析

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