Android消息机制的Handler+

本文深入探讨了Android消息机制的工作原理,重点分析了Message、MessageQueue、Looper之间的协作方式及其实现细节,包括消息的异步与同步处理、消息队列的管理与退出机制。

前面分析了Handler,MessageQueue,Looper之间是如何协作实现Android的消息机制的,但是还存在许多问题。比如:

  1. Looper.loop是一个死循环,它是怎样退出的?当MessageQueue中没有Message的时候会发生什么?

  2. Message的异步和同步是什么?在处理的时候有什么区别?

 

看源码的时候不要纠结于细节,重点看流程。前面就是看流程,省略了很多细节,现在就来看前面没看到的细节。

 

首先看一下Message的源码。

从构造函数看起,Message的构造函数是空的,注释提示说建议使用obtain()来获取Message。

// obtain从全局池里获取Message实例,这在很多情况下可以避免不必要的创建
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
// 除此之外,还有多个obtain(...args),但是都会调用这个obtain(),只是用传入的...args为获取到的Message设置参数

显然这所说的全局池是一个单链表结构,sPool是链表首部,sPoolSize是链表长度。obtain从全局池中取出一个Message并返回,同时将Message的flags置0,只有当全局池为空时才创建Message。既然有取出Message,自然也应该有存入Message,即recycleUnchecked

void recycleUnchecked() {    
    // 设置flags,标识Message被使用,并清除其他变量    
    flags = FLAG_IN_USE;    
    what = 0;arg1 = 0;arg2 = 0;obj = null;replyTo = null;sendingUid = -1;    
    when = 0;target = null;callback = null;data = null;    
    synchronized (sPoolSync) {        
        if (sPoolSize < MAX_POOL_SIZE) {            
            next = sPool;            
            sPool = this;
            sPoolSize++;
        }
    }
}

这个方法用于回收Message,设置待回收的Message的实例变量(但是将flags设置为FLAG_IN_USE,是表示回收之后才被使用吗?3⃣️),将其存入全局池。照例全局池的大小是有限制的,也需要注意同步。从方法名看这个方法没有做检查,检查是什么,看另一个方法recycle()

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

/*package*/ boolean isInUse() {
    return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}

由此可见,flags设置为FLAG_IN_USE表示Message被使用了,说明已经被回收了,就不要再回收了,这是检查。还有一个gCheckRecycle变量,这是适配用的,在updateCheckRecycle中设置,当API小于21时,将其设置为false。为什么要做这个适配呢?4⃣️

再看其他的方法,setAsynchronous将设置flags,将Message设置为异步模式,异步模式的Message不遵守Looper的同步屏障(synchronization barrier)。根据方法注释,某些操作比如view invalidation,可能在Looper的MessageQueue中设置同步屏障,以防止后续的Message在某些条件满足之前被传递。在view invalidation的情况下,调用android.view.View#invalidate发出的Messages会被各种同步屏障挂起,直到下一帧准备好绘制。同步屏障可以确保在恢复之前完全处理无效请求。

异步消息不受同步障碍的影响。它们通常表示中断、输入事件和其他必须独立处理的信号,即使其他工作已经挂起。尽管异步消息内部总是按顺序传递, 但相对于同步消息可能不按顺序传递。如果这些消息的相对顺序很重要, 那么它们不应该是异步的。

使用isAsynchronous方法可以判断Message是否是异步的。

/*
获取与此Message相关的任意数据的Bundle,并且在必要的时候懒惰创建它。调用setData(Bundle)来设置这个值。使用Messenger跨进程传递数据的时候,需要通过Bundle#setClassLoader(ClassLoader) Bundle.setClassLoader()来在Bundle上绑定ClassLoader,以便能够在检索他们的时候能够实例化对象。
*/
public Bundle getData() {
    if (data == null) {
        data = new Bundle();
    }

    return data;
}
//类似于getData,但是不能懒惰创建Bundle。如果Bundle没有存在,就返回null。
public Bundle peekData() {
    return data;
}
//使用Message持有的Handler发送自己。
public void sendToTarget() {
    target.sendMessage(this);
}
// 拷贝一个Message,不包括链表域next,sPoolSyns等,时间戳when和持有的Handler与回调方法Callback
public void copyFrom(Message o) {
    this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
    this.what = o.what;
    this.arg1 = o.arg1;
    this.arg2 = o.arg2;
    this.obj = o.obj;
    this.replyTo = o.replyTo;
    this.sendingUid = o.sendingUid;

    if (o.data != null) {
        this.data = (Bundle) o.data.clone();
    } else {
        this.data = null;
    }
}

接下来看一下没有涉及到的实例变量和类变量

/*
用户定义的message code,用于标志Message的用途。每个Handler都有它自己的变量空间,不用担心命名冲突
*/
public int what;
/*
arg1和arg2可以用来存储少量数据,如果数据多再用data(Bundle)
*/
public int arg1;
public int arg2;
/*
可以发送给接收方的对象。使用Messenger跨进程发送Message的时候,如果Message包含一个序列化(Parcelable,API8之前不支持)的框架类(framework class,不是应用实现的类)。传输其他数据则使用setData
*/
public Object obj;
/*
可选的Messenger,用于接收这个Message。具体的使用方法有发送者和接受者规定。
*/
public Messenger replyTo;
/*
可选的域,表明发送这个Messsage的uid,只对Messager发送的Messages有效。否则为-1。
*/
public int sendingUid = -1;
/*
Message触发的时间
*/
long when;
/*
Message自带的回调方法,前面讲Handler的时候提到过处理消息的方法dispatchMessage(Message)首先考虑调用的就是Message自带的回调方法。其次考虑调用MessageQueue的回调方法,最后才考虑Handler的handleMessage()
*/
Runnable callback;

Message分析完了,暂时不考虑跨进程的功能,接下来分析Looper和MessageQueue,因为MessageQueue是和Looper一起创建的,所以一起看比较方便。先看构造函数:

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

final MessageQueue mQueue;
final Thread mThread;
// MessageQueue的构造函数
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

// True if the message queue can be quit.
private final boolean mQuitAllowed;
@SuppressWarnings("unused")
private long mPtr; // used by native code

mQuitAllowed为true,表示MessageQueue能退出,在quit()里也能用到,主线程的MessageQueue的mQuitAllowed为false,不能退出,可以看到prepareMainLooper()中调用了prepare(false),最终传到MessageQueue的构造函数。而一般在子线程中创建Looper时,调用prepare(),默认设置设置mQuitAllowed为true,因此子线程的MessageQueue可以退出。

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

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

再来看Looper与MessageQueue的退出,Looper的退出有两种,内容是退出其持有的MessageQueues

public void quit() {
    mQueue.quit(false);
}

public void quitSafely() {
    mQueue.quit(true);
}

quitquitSafely的区别在于:

quit终止loop方法,不用处理MessageQueue里剩余的Messages。

quitSafely等待MessageQueue中剩余的Messages(不包括等待延迟的Messages)被处理完之后才终止loop方法。等待延迟的Messages指触发时间在当前时间之前但是还在MessageQueue中的Messages。

Looper退出之后,任何发送Message的方法(比如Handler#sendMessage(Message))都会返回false。

使用quit可能导致某些Messages不被处理,因此建议使用quitSafely

Looper的退出方法内部调用了MessageQueue的quit方法。

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

MessageQueue的实例变量mQuitting表示队列是否退出。这里最终调用本地方法nativeWake(mPtr)来退出队列。对于quit,移除MessageQueue中的所有Messages,对于quitSafely,移除MessageQueue中所有没有等待延迟触发的Messages。

private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {
            removeAllMessagesLocked();
        } else {
            Message n;
            for (;;) {// 找到MessageQueue中在现在之后触发的Messages
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();//回收Message
            } while (n != null);
        }
    }
}

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

接下来再看Looper的loop方法

public static void loop() {
    // 清空远程调用端的token,设置当前线程的token并存入ident
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // 如果Message为空,默认为MessageQueue为空,退出loop
            return;
        }
		// 当Message处理的时间超过slowDispatchThresholdMs时,就会报警
        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 {
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            //关闭跟踪日志
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (slowDispatchThresholdMs > 0) {
            final long time = end - start;//消息处理的耗时
            if (time > slowDispatchThresholdMs) {//消息处理超时了,报警
                Slog.w(TAG, "Dispatch took " + time + "ms on "
                       + Thread.currentThread().getName() + ", h=" +
                       msg.target + " cb=" + msg.callback + " msg=" + msg.what);
            }
        }

        // 再次获取当前线程的token,检查操作前后token是否有变化
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            // 报告一个异常 - What a Terrible Failure
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

private long mSlowDispatchThresholdMs;

这里的知识点有Binder通信时的token5⃣️,独立于方法的跟踪日志(Trace)6⃣️,还不了解,但是不影响理解。通过MessageQueue的next方法取出Message,如果Message为空则说明MessageQueue已经退出,退出Looper的loop方法。否则调用Message持有的Handler来处理Message。这里还指定了这个处理过程的耗时限制,如果超时了就会打印Warning日志。处理完之后调用Message的recycleUnchecked回收Message。

Handler也分析完了,现在从MessageQueue的next方法开始进入MessageQueue的源码分析。

Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
		// 在开始迭代前,做一些初始化,
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;//在下一个Message可以触发之前需要等待的时间
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();//清理等待的Binder指令
            }
			//等待nextPollTimeOutMillis再继续执行操作
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 如果Message不为空但是未持有Handler,则说明他被同步屏障拦截了,那么后面的同步Message都被同步屏障拦截了,可以先找异步Message来处理。
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //现在找到一个可以处理的Message了,不管他是同步的还是异步的
                if (msg != null) {
                    if (now < msg.when) {
                        // 如果Message触发的时间还没到,则说明现在没有可以执行的Message,需要等待,因此计算出等待的时间nextPollTimeoutMillis
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 获取一个Message,需要清除阻塞标志,并将Message标志为使用中
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // 如果已经quit了,则回收MessageQueue并返回null,Looper的loop接收到null,就知道MessageQueue退出了,会退出loop方法,能走到这一步说明MessageQueue中没有Message或者只有被同步屏障拦截的Message了
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // 如果Message队列还没有创建,或者所有Messages都还没有到触发的时间,则重新计算空闲的Handler的数量
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                // 如果没有空闲Handler,则设置阻塞标志,进入阻塞状态
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
				// 创建一组空闲Handler
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
			// 执行到这里说明没有可用的Message,但是Looper还没有退出,而且还存在空闲的Handler
			
            //采用相应的策略来决定是否回收空闲的Handler,这个策略由IdleHandler的具体类实现
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

这里的知识点在于,mPtr是什么?当它为0的时候,Looper已经退出了。可以发现处理同步Message的Handler是有限的,如果没有空闲的Handler,就会陷入阻塞。另外还有Binder.flushPendingCommands()nativePollOnceIdleHandler等。只有在Looper调用了quit之后MessageQueue的next才会返回null,Looper的loop才会退出,否则即使MessageQueue里已经没有Message了,loop也只是继续等待。

然后看enqueueMessage

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        // 如果MessageQueue已经退出了,那么回收这个新插入的Message,并返回false
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();//将Message标记为使用中
        msg.when = when;//设置Message的触发时间
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
			// 将Message插入到MessageQueue的队列头部,此时需要唤醒
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 按照触发时间的顺序将Message插入到MessageQueue的队列中部,如果之前由于缺少空闲Handler而引起阻塞,则需要唤醒
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            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.
        // 调用native方法来唤醒MessageQueue
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

参考:

https://www.jianshu.com/p/33c96606d604

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值