一张图总结Handler消息机制

文字总感觉很难描述,还不如来一张图总结一下来得清晰。


Handler

1.Looper.prepare()

创建Looper,并保存到sThreadLocal中,创建了一个MessageQueue并赋值给Looper#mQueue

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));
}

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

2.创建Handler

从ThreadLocal中获取线程的Looper赋值给Handler的mLooper,Looper的mQueue赋值给Handler的mQueue,所以Handler和其关联的Looper共同持有同一个MessageQueue。

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper(); //Looper赋值给Handler#mLooper
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; //Looper#mQueue赋值给Handler#mQueue
    mCallback = callback;
    mAsynchronous = async;
}

3.Looper.loop()开启消息循环

进入一个for(;;)循环,mQueue.next()取出消息后,交给Handler#dispatchMessage()来处理,当取出的Message为null时退出循环。以下为关键性源码。

public static void loop() {
    final Looper me = myLooper(); 
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    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 (;;) {
        Message msg = queue.next(); // might block
        //MessageQueue的next方法取出的消息为null,退出循环
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            //取出消息后交给Handler的dispatchMessage来处理
            msg.target.dispatchMessage(msg); 
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        msg.recycleUnchecked();
    }
}

从注释可以看到,MessageQueue#next()有可能导致线程阻塞,实际上是next()方法中的nativePollOnce()方法会阻塞线程。当处理完成一个Message后调用msg.recycleUnchecked()来回收消息,回收的消息将被缓存到sPool单链表的表头,最大缓存消息数量是50。当调用Handler#obtainMessage()时,就会从这个缓存链表中获取一个Message对象。

4.对外提供的消息回收接口:Message#recycle()

public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

5.MessageQueue

(1)MessageQueue 按Message.when降序排列,在前面的消息先出队(MessageQueue#enqueueMessage、MessageQueue#next)
(2)barrier的Message与普通Message的差别是target(类型是Handler)为null,只能通过MessageQueue#postSyncBarrier创建 barrier Message
(3)barrier的Message与普通Message以同样的规则进入队列,但是却只能通过MessageQueue#removeSyncBarrier出栈
(4)每个barrier使用独立的token(记录在Message#arg1)进行区分
(5)所有的同步消息(相对与异步消息而言,默认消息都是同步消息)如果barrier之后,都会被延后执行,直到调用MessageQueue#removeSyncBarrier通过其token将该barrier清除,当barrier在队头时,队列中的异步消息照常出队不受影响
(6)Handler中的对应构造函数被隐藏,但是可以通过调用Message#setAsynchronous指定对应的Message为asynchronous的Message。
(7)部署barrier(MessageQueue#postSyncBarrier)与清除barrier(MessageQueue#removeSyncBarrier)的相关方法都是隐藏的方法,对外不可见

6.使用Handler值得注意的事儿

(1)引起内存泄漏问题

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

异常:The following Handler class should be static or leaks might occur.

原因:
由于Handler有可能(绝大部分)会被投递到MessageQueue中的Message#target所引用,在此消息没有被处理的情况下将一直持有,此时非静态内部类的Handler持有外部类实例的引用,例如是一个Activity实例,如果此时Activity退出,将会由于被Handler强引用而无法及时GC,导致内存泄漏。

通常处理方法:
①将Handler改为静态内部类,使用弱引用来应用外部实例,只有若弱引用的对象,在GC时可以回收。关于Java中的四种引用,可以看看这篇文章《Java中的4种引用类型》。
②在外部类实例销毁时,调用Handler#removeMessage()将消息从消息队列中移除回收掉,这样就能移除Message对Handler的引用,当外部实例销毁时,Handler变成可被GC的对象。

(2)创建Handler时,提示当前线程没有Looper

Can't create handler inside thread that has not called Looper.prepare()

原因:
Looper.prepare()实际上是创建一个Looper传入作为所在线程的局部变量(全局由ThreadLocal与Thread#localValues来保证,简单参考ThreadLocal#get、ThreadLocal#set即可理解),而在真正Looper#loop的时候,是需要所在线程的局部变量的Looper为载体取得所有要处理的消息以及处理的方式的。因此创建Handler的同时是需要保证所在线程已经有了局部变量Looper的实例,才能保证Handler接下来真正运作。

通常处理方法:
在创建Handler前,主动调用下Looper.prepare()

每个线程的的Looper#prepare相对所在线程只能被调用一次,否则会报"Only one Looper may be created per thread"(参见Looper#prepare),之所以主线程直接创建Handler不会抛出类似异常,是因为在程序启动时,系统已经帮我们调用了Looper#prepare(参见ActivityThread#main)

7.消息池子

Message中有一个单链表的sPool用来缓存消息,最大缓存数量是50,所以当我们要使用Handler发送消息时,可以通过Handler#obtainMessage()或者Message.obtain()来得到一个消息对象,而不是通过new Message()来得到消息。

关于ThreadLocal可以看看这篇文章 https://www.jianshu.com/p/517f3d16ad89

你可能感兴趣的:(一张图总结Handler消息机制)