Handler,MessageQueue,Looper,你所不知道的Asynchronous
结合源码分析Handler还是可以异步的。首先介绍下 Android 消息处理机制相关的几个类:
1,Message:消息实体;
2,MessageQueue:消息队列;
3,Looper:消息循环体;
4,Handler:消息处理;
消息处理的机制,开始于 Looper.prepare(),Looper.loop(),那看看 Looper.loop() 的源码:
public static void loop() { //省略代码 final MessageQueue queue = me.mQueue; for (;;) { // 从MessageQueue拿到一个Message,queue.next()里面会阻塞cpu Message msg = queue.next(); if (msg == null) { // No message indicates that the message queue is quitting. return; } //省略代码 // msg.target 实际就是 Handler实例,dispatchMessage处理消息 msg.target.dispatchMessage(msg); //省略代码 msg.recycleUnchecked(); } }
有两个关键代码:一个 queue.next(),一个 msg.target.dispatchMessage(msg),先看 Handler.dispatchMessage() 方法:
public void dispatchMessage(Message msg) { if (msg.callback != null) { //如果Message.callback不空,实际也就是有Runnable,那么会直接运行它的run方法; handleCallback(msg); } else { if (mCallback != null) { // 如果new Handler的时候,传了Callback,会调用Callback.handleMessage if (mCallback.handleMessage(msg)) { return; } } //Message没有Runnable,Handler没有Callback,或者Callback.handleMessage 返回false,才会调用Handler自己的handleMessage handleMessage(msg); } }
接下来看 MessageQueue.next() 方法:
Message next() { for (;;) { //省略代码 // @1 // native层阻塞cpu nativePollOnce(ptr, nextPollTimeoutMillis); //省略代码 synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // @2 // msg.target为空的情况,只有MessageQueue.enqueueSyncBarrier // msg.isAsynchronous 只能结合 Barrier 使用,也就是说就算有异步的消息,但是没有设置 Barrier,也是没效果的。 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // @3 //有消息,但是还没到时间,最后转到 继续阻塞cpu(也就是上面的注释@1) nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { mBlocked = false; if (prevMsg != null) { // @4 // prevMsg不空,mMessages是不会变的,所以如果有Barrier,会始终尝试去拿异步消息(也就是上面的注释@2) // 所以记得设置了Barrier,要对应的移除掉它,调用 MessageQueue.removeSyncBarrier prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; return msg; } } else { nextPollTimeoutMillis = -1; } //省略代码 } // @5 // 消息队列空闲的时候,也就是有消息但是还没到时间,或者没有消息的时候,会调用IdleHandler.queueIdle() // 可以通过 MessageQueue.addIdleHandler方法添加 for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } //省略代码 } }
为什么 msg.target 为空的情况,只有 MessageQueue.enqueueSyncBarrier?
因为正常上 Handler 的 sendMessage 或者是 Message 的 sendToTarget ,实际最后都是走到
MessageQueue.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) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; 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; 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; }
从上面代码就可以看出,正常的 sendMessage 如果 target 为空,会抛出异常的。
反而 MessageQueue.enqueueSyncBarrier:
int enqueueSyncBarrier(long when) { synchronized (this) { final int token = mNextBarrierToken++; final Message msg = Message.obtain(); msg.markInUse(); msg.when = when; msg.arg1 = token; Message prev = null; Message p = mMessages; if (when != 0) { while (p != null && p.when <= when) { prev = p; p = p.next; } } if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; } }
从上面代码可以看出,Barrier也就是个Message,是没有target的。
怎么让 msg.isAsynchronous 呢?
有两种方式:
1,Message的方法setAsynchronous();
2,Handler的构造方法,可以传入 async 参数;
public Handler(Handler.Callback callback, boolean async) { //省略代码 mLooper = Looper.myLooper(); mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { // Handler的sendMessage 会把 msg 赋值成 Asynchronous msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
总结:
1,消息队列循环执行,不一定是完全按照时间串行执行的,是可以有异步消息的。
2,异步消息 必须结合 Barrier 使用,也就是说就算有异步消息,但是没有设置 Barrier,也是没效果的。
3,设置了Barrier,要对应的移除掉它,否则同步消息再也不会被处理。
更多相关文章
- Android(安卓)Studio adb无法启动解决方案
- Android(安卓)运行时找不到类:java.lang.NoClassDefFoundError
- 迁移到AndroidX的现实
- android接入原生第三方登录(微信登录、QQ登录、新浪微博登录)
- Android中 Webview中js与Activity的交互
- Unable to start activity ComponentInfo
- Android(安卓)五大布局
- Android之发送短信的两种方式
- ArcGis for Android(安卓)10.2.8个人整理