结合源码分析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,要对应的移除掉它,否则同步消息再也不会被处理。



更多相关文章

  1. Android(安卓)Studio adb无法启动解决方案
  2. Android(安卓)运行时找不到类:java.lang.NoClassDefFoundError
  3. 迁移到AndroidX的现实
  4. android接入原生第三方登录(微信登录、QQ登录、新浪微博登录)
  5. Android中 Webview中js与Activity的交互
  6. Unable to start activity ComponentInfo
  7. Android(安卓)五大布局
  8. Android之发送短信的两种方式
  9. ArcGis for Android(安卓)10.2.8个人整理

随机推荐

  1. Android(安卓)SystemUI任务栏修改
  2. android indication
  3. Android颜色渐变的分隔线(ListView)
  4. android 中 checkBox 的使用
  5. Android(安卓)深入研究LBS(基于位置的服务
  6. Android:霓虹灯
  7. 三步搞定:Vue.js调用Android原生方法
  8. Android之Loader理解
  9. 修改进度条ProgressBar颜色
  10. 2011.07.11——— android 自定义toast