05.异步消息机制之深入Handler源码(上)

148 阅读3分钟

上一篇写了Handler的原理和简单使用,这一篇我们来看看Handler的源码。

先来看看Looper和MessageQueue的关系,以及主线程和子线程的Looper有何异同。

1、Looper和MessageQueue的关系

我们先从Handler的实现入手,先看看Handler(Looper)的实现,该方法实际调用了构造函数Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async),源码如下:

public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

从该构造函数的源码可以看到,参数looper不仅提供了Looper对象,还提供消息队列MessageQueue。所以异步消息机制中的消息队列MessageQueue是由Looper提供的。

2、主线程和子线程Looper的异同

再来看看不同线程Looper的获取:

//主线程
Looper mainLooper = Looper.getMainLooper()

//子线程
Looper.prepare()
Looper threadLooper = Looper.myLooper()            

主线程的Looper可以直接获取,不需要显示初始化,子线程需要先调用Looper.prepare()初始化Looper。子线程Looper初始化的调用链如下:

//根据注释,必须先调用prepare()方法,才能调用Looper.loop()方法,结束后需要调用quit()退出
public static void prepare() {
        prepare(true);    //此处参数为true
    }

private static void prepare(boolean quitAllowed) {
        //当Looper已存在,抛出异常,一个线程只能有一个Looper
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

//Looper构造方法
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

//MessageQueue构造方法
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

//mQuitAllowed唯一一处使用的地方
void quit(boolean safe) {
        if (!mQuitAllowed) {
            //抛出主线程不允许退出的异常
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

顺着调用链可以看到,子线程Looper创建时调用了prepare(quitAllowed)方法,该方法调用构造函数Looper(quitAllowed),将创建的Looper存放进sThreadLocal。Looper(quitAllowed)构造方法内new了一个MessageQueue,传入boolean值为true的参数quitAllowed。参数quitAllowed如果为false,调用quit()会抛出异常,提示“主线程不允许退出”。该参数也和主线程有关,那我们再来看看主线程的Looper是如何初始化的,从Looper.getMainLooper()往上追溯:

public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

//直接找到创建sMainLooper的方法;
//根据注释可知,该方法将初始化当前线程的Looper,并将该Looper标记为应用的主Looper
public static void prepareMainLooper() {
        prepare(false);    //此处参数是false
        synchronized (Looper.class) {
            //主线程同样只允许存在一个Looper
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

//定位到myLooper()方法
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

Looper.getMainLooper()调用了prepareMainLooper(),prepareMainLooper()也调用了prepare(quitAllowed)方法进行初始化。但传入的参数值是false。所以参数quitAllowed参数其实用来区分当前线程是否为子线程的 ,子线程中quitAllowed默认为true,当调用quit()可以直接退出。主线程中该方法为false,无法调用quit()结束loop()

prepare(quitAllowed)将创建的Looper存放进sThreadLocal,prepareMainLooper()调用了myLooper()方法,该方法放回的是存放在sThreadLocal中的Looper。

所以主线程和子线程的Looper是没有差别的,只是主线程的Looper会默认初始化,且不能调用quit()直接退出loop()

注意:

一个线程只能有一个Looper,一个Looper拥有一个MessageQueue;

主线程的Looper由系统初始化,子线程的Looper需要显示调用Looper.prepare()初始化,且不能重复初始化。