[Handler系列四] Looper全解析

Looper

稍微了解Handler的开发者,基本都知道Looper是干什么的,没猜错,就是为了循环获取消息队列里的消息,然后通过Handler的dispatchMessage()方法里进行消息的处理。

初始化Looper

一般情况下进行对象的初始化,都是通过构造方法来进行初始化的

private Looper(boolean quitAllowed) {
    //初始化一个消息队列
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
复制代码

源码可以看出,构造方法是private私有化的,所以不能直接在外部进行new来创建Looper对象;通过跟踪构造方法哪里进行调用了,可以看到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");
    }
    //创建一个Looper对象,并与当前线程进行绑定
    sThreadLocal.set(new Looper(quitAllowed));
}
复制代码

代码很简单,就是先通过sThreadLocal进行获取,不为null直接抛出异常,为null直接创建一个Looper对象,并保存在sThreadLocal里边,关于ThreadLocal下篇文章我们再详细介绍,这里只做简单的说明就是

实现一个线程本地存储,是什么呢?一个让每个线程都有拥有value的变量。所有的线程都共享同个ThreadLocal对象,但每个线程在这拿到的value都不相同,并且一个线程在这里做的改变并不影响其他线程。支持值为null

这里可以引出一些常见的面试题

  1. 一个线程中可以多少个Looper对象

  2. 一个Looper对象可以有多少个消息队列

轮询消息队列loop()

这个没啥好说的,直接上源码,源码里的内容有些长,但是经过删减,最终的流程大致如下:

public static void loop() {
    //首先拿到Looper对象
    final Looper me = myLooper();
    
    //如果Looper对象为空,直接抛出异常,
    //调用loop方法前,必须在当前线程中先调用Looper.prepare()来生成Looper对象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    
    //Looper的构造方法里创建的消息队列
    final MessageQueue queue = me.mQueue;

    //开启一个死循环
    for (;;) {
    	//通过MessageQueue的next方法来从消息队列中取消息
        Message msg = queue.next(); // might block
        
        //如果取出的消息为空,则说明消息队列已经退出了,则结束死循环
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
				
        //省略部分代码
	.....
				
	//交给msg.target即handler来处理该消息
        msg.target.dispatchMessage(msg);
				
	//最后将该消息进行回收
        msg.recycleUnchecked();
    }
}
复制代码
  1. 首先判断Looper对象是否为空,这里说明在当前线程调用Looper.loop()前,必须先调用Looper.prepare()方法来创建一个Looper对象
  2. 开启一个死循环,然后通过MessageQueue的next()方法来取出消息来,这里可以详细查看【Handler系列二】 MessageQueue,如果取出的消息为null,则说明当前消息队列已经终止,则直接退出死循环
  3. 将取出的消息交给msg.target的dispatchMessage()方法进行处理,这里详细可以查看【Handler系列三】 Handler全解析 ,msg.target其实就是在Handler.obtainMessage()的时候,将当前Handler赋值给该消息的
  4. 最后将该消息进行回收

总结

Looper其实特别简单,就是提供一个死循环,然后从MessageQuene中获取消息,然后交给Handler来处理该消息,处理完成后,将该消息进行回收。

源码虽然简单,每次面试都会碰到关于Looper的面试题

  1. 一个线程可以有多个Looper对象,多少个消息队列,多少个Handler对象
  2. Looper在主线程开启死循环,为什么不会导致卡死

最后,欢迎各位读者留言来回答这两个面试题哦!!!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享