起因

项目上遇到一个问题A应用进入到B应用,A应用窗口过了很久才关闭。
A应用跑过来说,我没有消失是因为系统一直没有调用我的Stop周期
B应用跑过来说,我主线程没有耗时resume早就走完了。我没有耗时,是你系统没去通知它,关我什么事

日志分析

从此处看 AMS 已经开始有线程拉起B应用
12-20 16:47:11.283  1029 23757 I wm_set_resumed_activity: [0,space.B包/B的窗口,resumeTopActivity]
12-20 16:47:11.294  1029 23757 I wm_resume_activity: [0,183595706,538,A包/B的窗口]

进程 16235 为B应用进程,此时B进程已经完成resume的生命周期执行完成
12-20 16:47:11.322 16235 16235 I wm_on_activity_result_called: [183595706,B的窗口,ACTIVITY_RESULT]
12-20 16:47:11.326 16235 16235 I wm_on_resume_called: [183595706,B的窗口,RESUME_ACTIVITY]
12-20 16:47:11.343 16235 16235 I wm_on_top_resumed_gained_called: [183595706,B的窗口,topWhenResuming]

10秒之后 ActivityTaskManager 提示 B应用 启动超时。
12-20 16:47:21.272  1029  1213 W ActivityTaskManager: Launch timeout has expired, giving up wake lock!

AMS 超时后强制结束A窗口
12-20 16:47:21.295  1029  1213 I wm_destroy_activity: [0,140682972,538,A包/A的窗口,finish-idle]
12-20 16:47:21.301  1029 21512 W ActivityTaskManager: Duplicate finish request for r=ActivityRecord{862a6dc u0 A包/A的窗口 t538 f}}

A应用进程 4794 开始执行 Stop 生命周期
12-20 16:47:21.302  4794  4794 I wm_on_stop_called: [140682972,A的窗口,LIFECYCLER_STOP_ACTIVITY]

系统卡顿了吗?

从日志看B应用 resume 执行完成了,从系统层面上来讲,此时系统应该去调用A应用的Stop生命周期。但是却迟迟没有调用,最终超时导致A应用窗口没有被关系,被强制调用 Stop,才彻底关闭了A应用。

系统直呼冤枉,超时不是因为系统卡顿而没有去调用,如果是系统卡顿了,那么系统所有调用都会超时,整体会出现卡顿情况。

代码分析

首先我们要知道系统是如何判定一个应用是如何执行完 onRusume 或者其他生命周期的。
ActivityThread.java 中有个叫 IdleHandler 的玩意,在 onRusume 执行完成后会通过它去发送消息告知AMS我执行完了

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
           
        //此处省略无数代码......

        r.nextIdle = mNewActivities;
        mNewActivities = r;
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
    }
    private class Idler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            //此处省略无数代码......

            am.activityIdle(a.token, a.createdConfig, stopProfiling);
            a.createdConfig = null;
            
            //此处省略无数代码......
            return false;
        }
    }

什么是IdleHandler

  • IdleHandler是android.os.MessageQueue的一个内部接口
  • IdleHandler会在MessageQueue中没有Message要处理或者要处理的Message都是延时任务的时候得到执行

看到这里各位心里有点数,主线程的Looper中,自己 MessageQueue 是由管理机制的。当你一个任务执行时间过程系统会直接给你报ANR这个是总所周知的问题。

但是不耗时,你就可以肆无忌惮的在主线程乱来吗?并不是,IdleHandler在MessageQueue中还需要等待执行,请看代码注释分析

    Message next() {
        //省略代码......
        for (;;) {
            //省略代码

            synchronized (this) {
                //省略代码......
                // 当mMessages链表为null或者下一个需要执行的Message是个delay任务的时候,开始统计IdlerHandler数量
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                // 如果没有IdlerHandler,则进行下一个循环
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
                // 新建一个数组,用来复制存放IdleHandler,因为后面会对mIdleHandlers进行remove元素操作
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // 开始执行IdleHandler
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                //省略代码,取出 IdleHandler 内容开始执行
            }

            //省略代码......
        }
    }

真相大白了,如果此时主线依旧由大量的业务需要处理那么就没有机会给AMS发送消息。最终导致其他模块无法正常收到AMS回调。

合理使用主线程

其实不止这一个场景,系统发送过来广播也是如此,很多场景下系统发送过来的任务也是在主线程中的MessageQueue被执行,如果队列中任务过多,你收到广播消息也会延后,不要张口就来,说系统卡了。

        public final void scheduleReceiver(Intent intent, ActivityInfo info,
                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                boolean sync, int sendingUser, int processState) {
            updateProcessState(processState, false);
            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                    sync, false, mAppThread.asBinder(), sendingUser);
            r.info = info;
            r.compatInfo = compatInfo;
            sendMessage(H.RECEIVER, r);
        }

大部分人写代码只考虑说我没有耗时行为,所以不是自己问题。但是不合理使用主线程的消息队列也会导致各种卡顿或者延迟等问题

最后修改:2024 年 04 月 23 日
如果觉得我的文章对你有用,请随意赞赏