Handler屏障消息和IdleHandler

本文详细解释了Android中的屏障消息如何提升异步消息的执行优先级,包括发送屏障消息、异步消息的发送与移除,以及IdleHandler在消息空闲时的处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.屏障消息

屏障消息也叫同步屏障消息,作用是提升异步消息的执行优先级的。
首先假设有这样一种场景,在主线程的MessageQuee里面存在很多个消息在排队等待处理,然后这个时候我们去给主线程发送了一条刷新UI的Message,那么这条消息就需要高优处理。
为了实现这一效果,我们需要做3件事情:

1.1.发送屏障消息

给对应的MessageQueue发送一条屏障消息,以ViewRootImpl的scheduleTraversals方法举例:

 //发送同步屏障消息
  mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

这里的mTraversalBarrier是返回回来的一个token标志,在后续使用完屏障消息后,需要用这个token去移除屏障消息。

1.2. 发送一条异步消息

首先我们需要知道的是,我们正常往handler发送的消息都是同步消息,即Message中的变量isAsynchronous是false的,只有当isAsynchronous的值是 true,才表示是一个异步消息。
我们可以通过设置这个属性,来标记消息是否是异步消息:

Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
1.3. 移除屏障消息

使用完屏障消息后,需要手动移除,避免不能处理同步消息的情况发生:

//移除同步屏障消息
         mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
1.4.屏障消息的运行逻辑
Message next() {
    ......
    int nextPollTimeoutMillis = 0;
    for (;;) {
        ......
        
        // 阻塞,nextPollTimeoutMillis为等待时间,如果为-1则会一直阻塞
        nativePollOnce(ptr, nextPollTimeoutMillis);
        
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            // 下面的代码目的是获取一个可用的消息,如果找到就return,
            // 没找到就继续后面我省略的代码(省略了IdHandler的相关代码)
            
            // 获取时间,还是通过uptimeMillis这个方法
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            // 如果队列头部消息为屏障消息,即“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.
                    // 一个标记,表示next循环是不是还在被阻塞着
                    mBlocked = false;
                    // 移除消息
                    if (prevMsg != null) {
                        // 移除异步消息
                        prevMsg.next = msg.next;
                    } else {
                        // 移除同步消息
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    
                    // 标记为正在使用
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                // 没有获取到消息,接下来运行下面省略的代码,nextPollTimeoutMillis为“-1”,在循环开始的nativePollOnce方法将会一直阻塞。
                nextPollTimeoutMillis = -1;
            }
        
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
            ......// IdleHandler
        }// end of synchronized
        ......// IdleHandler
    }
}
        

在获取下一条执行消息的时候,如果存在target为null的消息,那么就是屏障消息,这个时候会忽略掉队列里面的排在前面的同步消息,优先去任务队列里面找寻isAsynchronous为true的移步消息,并返回。

2.IdleHandler

IdleHandler会在Looper消息处理空闲的时候去执行,消息空闲包括消息队列为空,或者最近需要执行的消息比当前时间晚。

2.1.基本使用方法
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                Log.v(TAG, "#looper message has worked out");
                return false;
            }
        });
    }

这里返回false,表示只会执行一次回调;返回true,则每次Looper空闲的时候都会去执行。

2.2.内部管理

在MessageQueue中,有一个集合来管理IdleHandler,用来增加或者删除:

private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<>();

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

public void removeIdleHandler(@NonNull IdleHandler handler) {
    synchronized (this) {
        mIdleHandlers.remove(handler);
    }
}
2.3.实现原理

处理IdleHandler的代码,同样是在Message.next()方法里面:

    Message next() {
        ...
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
       
            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 屏障消息处理,获取异步消息
                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());
                }
                // 注释2 获取到Message不为null,则说明存在需要处理的Message
                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.
                    // MessageQueue无消息,设置延迟为-1,nativePollOnce无限等待,直到有消息
                    nextPollTimeoutMillis = -1;
                }

                // 注释3 执行到此处,说明没有需要执行的Message(MessageQueue为空,或者存在延迟Message)

                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                // 注释4 IdleHandler队列为空,不执行,进入下一个循环,此后不再会执行IdleHandler判断,除非下次进入next方法
                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)];
                }
                // 注释5 mIdleHandlers数组赋值给 mPendingIdleHandlers
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // 注释6 执行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 {
                    // 注释7 执行任务逻辑,并返回任务释放移除
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        // 注释8 移除任务,下次不再执行
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // 注释9 重置IdleHandler的Count为0,避免下次重复执行IdleHandler队列
            pendingIdleHandlerCount = 0;
        }
    }

需要注意的是,IdleHandler内部的执行,也是在当前线程执行的,如果是在UI线程中,同样也不能做太过耗时的处理,以免发生ANR的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值