美文网首页
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