【17】Android 线程间通信(二) - Handler

概述

记得上篇文章我们留下的问题吗?如果还没有看过上一篇讲解Handler基本原理文章的同学可以补一下知识。Android 线程间通信(一) - Handler

回到正题,这次我们将一下上一篇文章留下问题中的几个,idleHandler & syncBarrier。

同步屏障

首先我们了解吗,我们平时通过Handler#sendMessage发送的属于什么消息?同步消息还是异步消息。既然这次讲到同步屏障,我们也可以大胆的猜想,我们平时发送的消息是同步消息,只有特殊情况下,插入这个同步屏障,才会走特殊的异步方式。
如果能够这样推导,说明在Handler这一块的了解已经比较深刻了。我们继续看一下源码。

	//MessageQueue.java
    @UnsupportedAppUsage
    Message next() {
        ...
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            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;
                //(1) msg不为空,且msg.target为空才会进入
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a 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;
                }
		...
    }

我们看代码中(1)注解的位置,msg不为空这个很好理解,消息需要有实体,但是target为空这个是为什么呢?上一片文章讲过,msg的target其实就是Handler。而下面官方的注解说明,被栏栅挡住了,在队列中查找下一个异步消息。然后开始从当前这个msg的下一个msg开始查找,知道下一个消息属于异步消息或者查找直到为空位置,跳出循环。

可见挡住同步消息的,正是这个msg.target=null的消息,那么它是怎么插入进来的呢?我们接着看。

	//MessageQueue.java
	
    @UnsupportedAppUsage
    @TestApi
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        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;
        }
    }

MessageQueue中有一个方法叫做postSyncBarrier,可以理解为插入同步屏障。首先通过Message#obtain获取消息,这里是对消息对象的一个复用,在消息处理完之后,会把Message的target清除。然后标记为使用,接着就是消息的一些属性赋值,但唯独没有重新确定msg的target对象。可见如果messageQueue需要插入同步屏障,就是通过这个方法把屏障消息插入进去的。

插入同步屏障消息是直接通过MessageQueue进行插入

既然有插入同步屏障,那么也有移除屏障,让同步消息可以继续执行。

	//MessageQueue.java
    @UnsupportedAppUsage
    @TestApi
    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

还有个问题,就是同步屏障是挡住同步消息,那么怎么发送异步消息呢?我们看看Message的源码。

	// Message.java
    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }

有一个设置异步的方法,根据传入的bool值,flags和标志位做位运算。我们只要了解,当async为true,flags = 1,async为false,flags = 0。之前跳出next中的do…while循环中,判断异步消息的方式,就是msg#isAsynchironous。也就是说,setAsynchronous当传入true的时候,判断消息的条件就为真,此时发送的消息就是异步消息了。

	//Message.java
    public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }

默认情况下flags = 0,为同步消息

这样一来,同步屏障的作用和使用方式,我们就彻底搞懂了。

IdleHandler

空闲Handler,和字面意思一样。Handler是消息处理的句柄,那么IdleHandler,就是处理空闲消息的句柄。在MessageQueue#next源码中,我们继续看一下。

	//MessageQueue#next
                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            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;
        }

next方法下面有这样一段代码,什么时候会执行下去呢?就是当msg=null的时候,因为当msg不为空,说明取出了msg,并返回给Looper进行后续分发了。msg为null,说明此时消息队列中取不出消息了,就会从mIdlehandlers这个集合中,获取集合大小,并且把集合转换成数组mPendingIdleHandlers。

接着通过一个for循环,取出每一个idler并执行它的queueIdle,这个queueIdle是一个接口,可见是一个接口回调的操作,回调给用户去操作了。

	//MessageQueue.java
    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

通过MessageQueue的addIdleHandler接口,我们可以向MessageQueue维护的集合中添加这种空闲Handler,当消息队列MessageQueue的消息队列中没有任务可以执行之后,就会执行这些空闲的消息。

可以发现,IdleHandler适合处理那些优先级比较低的事情,而不会影响到用户体验。也可以避免这些任务与高优先的任务资源竞争,从而优化性能和响应速度。还可以通过它来管理资源,确认在队列空闲时期去释放不再需要的资源。

总结

1、MessageQueue可以发送同步屏障消息
2、Message可以通过设置确认发送的消息为同步还是异步
3、同步屏障消息的Handler为空
4、MessageQueue可以添加IdleHandler来处理优先级较低的任务

这下应用层Handler的东西我们都了解了。下一篇文章接下来将通过native层来分析一下Handler这个线程间通信的方式,底层到底还做了什么。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值