Activity启动流程总结-超时判定机制

前言

在Activity启动过程中,ActivityManagerService会与APP进程进行交互,调度APP进程上的Activity的生成和初始化,以及生命周期的逐步切换和回调。

在这期间的调度交互中ActivityManagerService需要知道APP进程端的执行结果来进行下一步操作,因此ActivityManagerService需要一个超时判定机制,来应对APP进程端执行过久或无响应时的情况。

例如,当目标Activity所属进程未启动时,ActivityManagerService需要先请求Zygote创建进程,然后等待进程创建完毕后继续启动目标Activity,那么AMS如何监听进程启动是否太久呢?还有当启动目标Activity前,会先pause当前正显示的Activity,之后才显示目标Activity,那么AMS是否会无限期等待?

源码探究

文中源码基于Android 9.0

ActivityManagerService在调度Activity的各个阶段都需要超时处理,这里按照《Activity启动流程总结-生命周期》中的过程来分阶段看。

Activity pause阶段

Activity启动流程中,首先需要暂停当前正在显示的Activity。

超时的设置

[ActivityStack.java]

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
        ActivityRecord resuming, boolean pauseImmediately) {
    // ···
    ActivityRecord prev = mResumedActivity;
    // ···
    mPausingActivity = prev;
    // ···
    if (prev.app != null && prev.app.thread != null) {
        try {
            // 调度APP进程执行对应Activity的pause行为
            mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
                    PauseActivityItem.obtain(prev.finishing, userLeaving,
                            prev.configChangeFlags, pauseImmediately));
        } catch (Exception e) {
            // Ignore exception, if process died other code will cleanup.
            Slog.w(TAG, "Exception thrown during pause", e);
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }
    } else {
        mPausingActivity = null;
        mLastPausedActivity = null;
        mLastNoHistoryActivity = null;
    }
    // ···
    
    // If we are not going to sleep, we want to ensure the device is
    // awake until the next activity is started.
    if (!uiSleeping && !mService.isSleepingOrShuttingDownLocked()) {
        // 获取Wakelock并设置超时监听
        mStackSupervisor.acquireLaunchWakelock();
    }

    if (mPausingActivity != null) {
        // ···
        // pauseImmediately默认为false
        if (pauseImmediately) {
            // If the caller said they don't want to wait for the pause, then complete
            // the pause now.
            completePauseLocked(false, resuming);
            return false;

        } else {
            // 设置超时监听
            schedulePauseTimeout(prev);
            return true;
        }

    } else {
        // This activity failed to schedule the
        // pause, so just treat it as being paused now.
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
        if (resuming == null) {
            mStackSupervisor.resumeFocusedStackTopActivityLocked();
        }
        return false;
    }
}

该方法中会设置两个超时,在通过scheduleTransaction方法调度Activity进行pause后,就调用acquireLaunchWakelockschedulePauseTimeout方法进行超时设置。

LAUNCH_TIMEOUT_MSG

[ActivityStackSupervisor.java]

void acquireLaunchWakelock() {
    if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
        throw new IllegalStateException("Calling must be system uid");
    }
    mLaunchingActivity.acquire();
    if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
        // To be safe, don't allow the wake lock to be held for too long.
        mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
    }
}

可以看出这里是通过mHandler发送延迟消息的方式来进行超时设置,what为LAUNCH_TIMEOUT_MSG,LAUNCH_TIMEOUT为10s

PAUSE_TIMEOUT_MSG

[ActivityStack.java]

private void schedulePauseTimeout(ActivityRecord r) {
    final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
    msg.obj = r;
    r.pauseTime = SystemClock.uptimeMillis();
    mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
}

通过mHandler发送延迟消息的方式来进行超时设置,Message的what赋值为PAUSE_TIMEOUT_MSG,obj为需要pause的ActivityRecord,pauseTime为系统启动以来时间。

[ActivityStack.java]

private static final int PAUSE_TIMEOUT = 500;

PAUSE_TIMEOUT表示500ms,留给Activity pause的时间很短,在500ms后就会触发超时。

mHandler的由来
  • ActivityStack的mHandler成员在ActivityStack的构造函数中实例化:
    [ActivityStack.java]
ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
        int windowingMode, int activityType, boolean onTop) {
    mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
}

mHandler实例的类型为ActivityStackHandler(继承自Handler),通过ActivityManagerService的mHandler的成员的Looper创建。

  • ActivityStackSupervisor的mHandler成员在构造函数中创建,其实例为ActivityStackSupervisorHandler
    [ActivityStackSupervisor.java]
public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
    mService = service;
    mLooper = looper;
    mHandler = new ActivityStackSupervisorHandler(looper);
}
  • ActivityManagerService的mHandler成员在构造函数中实例化,其实例为创建MainHandler
    构造函数中创建ServiceThread(继承自HandlerThread)并启动,之后将Looper设置给mHandler。通过mHandler发送的消息将运行在ServiceThread中。

[ActivityManagerService.java]

public ActivityManagerService(Context systemContext) {
    mHandlerThread = new ServiceThread(TAG,
            THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
    mHandlerThread.start();
    mHandler = new MainHandler(mHandlerThread.getLooper());
}

超时的触发

LAUNCH_TIMEOUT_MSG

进入ActivityStackSupervisorHandler的handleMessage方法的LAUNCH_TIMEOUT_MSG case:
[ActivityStack.java]

public void handleMessage(Message msg) {
    switch (msg.what) {
        case LAUNCH_TIMEOUT_MSG: {
            synchronized (mService) {
                if (mLaunchingActivity.isHeld()) {
                    Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
                    if (VALIDATE_WAKE_LOCK_CALLER
                            && Binder.getCallingUid() != Process.myUid()) {
                        throw new IllegalStateException("Calling must be system uid");
                    }
                    // 释放Wakelock
                    mLaunchingActivity.release();
                }
            }
        } break;
        // ···
    }
}
PAUSE_TIMEOUT_MSG

进入ActivityStackHandler的handleMessage方法的PAUSE_TIMEOUT_MSG case:
[ActivityStack.java]

public void handleMessage(Message msg) {
    switch (msg.what) {
        case PAUSE_TIMEOUT_MSG: {
            // 获取pausing的Activity对应的ActivityRecord
            ActivityRecord r = (ActivityRecord)msg.obj;
            // We don't at this point know if the activity is fullscreen,
            // so we need to be conservative and assume it isn't.
            Slog.w(TAG, "Activity pause timeout for " + r);
            synchronized (mService) {
                if (r.app != null) {
                    mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
                }
                // 传入token和true
                activityPausedLocked(r.appToken, true);
            }
        } break;
        // ···
    }
}

当过了500ms的延迟时间后,若触发了PAUSE_TIMEOUT_MSG的消息,将执行activityPausedLocked方法,传入Activity对应的窗口token和true(表示来自超时)。

activityPausedLocked方法在正常的Activity pause后也会调用。

接着进入activityPausedLocked方法:
[ActivityStack.java]

// timeout在超时触发情况下为true,正常pause情况下为false
final void activityPausedLocked(IBinder token, boolean timeout) {
    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
        "Activity paused: token=" + token + ", timeout=" + timeout);

    final ActivityRecord r = isInStackLocked(token);
    if (r != null) {
        // 移除PAUSE_TIMEOUT_MSG消息
        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
        if (mPausingActivity == r) {
            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
                    + (timeout ? " (due to timeout)" : " (pause complete)"));
            mService.mWindowManager.deferSurfaceLayout();
            try {
                completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
            } finally {
                mService.mWindowManager.continueSurfaceLayout();
            }
            return;
        } else {
            // ···
        }
    }
    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}

可以看出该方法中是否timeout的区别仅在日志不同上,即使超时也会继续进行后续的启动Activity的流程。

超时的取消

当APP进程端完成Activity的pause操作后,会调用ActivityManagerService的activityPaused方法,而在该方法中又会通过token查找对应的ActivityStack,调用其activityPausedLocked方法,此时传入的timeout为false,在activityPausedLocked方法中就会移除PAUSE_TIMEOUT_MSG消息。

LAUNCH_TIMEOUT_MSG消息将会在目标Activity启动完成后取消。

Application launch阶段

若待启动的目标Activity所属进程未启动,则AMS会先请求创建进程。

超时的设置

ActivityManagerService中会调用startProcessLocked方法进行请求创建进程:
[ActivityManagerService.java]

private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
        ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
        String seInfo, String requiredAbi, String instructionSet, String invokeWith,
        long startTime) {
    // ···
    // 进一步请求创建进程
    final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
            app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
            requiredAbi, instructionSet, invokeWith, app.startTime);
    synchronized (ActivityManagerService.this) {
        // 这个方法中进行超时设置
        handleProcessStartedLocked(app, startResult, startSeq);
    }
    // ···
}

handleProcessStartedLocked方法中又会调用另一个重载方法:
[ActivityManagerService.java]

private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
        long expectedStartSeq, boolean procAttached) {
    synchronized (mPidsSelfLocked) {
        this.mPidsSelfLocked.put(pid, app);
        // procAttached传入为false
        if (!procAttached) {
            // what为PROC_START_TIMEOUT_MSG
            Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
            // obj为待启动进程对应的ProcessRecord
            msg.obj = app;
            // 发送延迟消息
            mHandler.sendMessageDelayed(msg, usingWrapper
                    ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
        }
    }
    checkTime(app.startTime, "startProcess: done updating pids map");
    return true;
}

这里可以看出是通过ActivityManagerService的mHandler成员发送一个what=PROC_START_TIMEOUT_MSG10s延迟消息。

[ActivityManagerService.java]

// 20min
static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
// 10s
static final int PROC_START_TIMEOUT = 10*1000;

当有对启动进程过程附加监听时,将设置延迟时间为1200s,例如设置了Valgrind内存监测工具。通常情况下延迟时间为10s。

超时的触发

ActivityManagerService的mHandler是在ActivityManagerService构造函数中初始化,其实例为MainHandler,进入它的handleMessage方法:
[ActivityManagerService.java]

public void handleMessage(Message msg) {
    switch (msg.what) {
        // ···
        case PROC_START_TIMEOUT_MSG: {
            ProcessRecord app = (ProcessRecord)msg.obj;
            synchronized (ActivityManagerService.this) {
                processStartTimedOutLocked(app);
            }
        } break;
        // ···
    }
}

在PROC_START_TIMEOUT_MSG case中调用了processStartTimedOutLocked方法:
[ActivityManagerService.java]

private final void processStartTimedOutLocked(ProcessRecord app) {
    final int pid = app.pid;
    // 标记是否进行清理工作
    boolean gone = false;
    synchronized (mPidsSelfLocked) {
        // mPidsSelfLocked中保存着所有在运行的APP进程信息
        ProcessRecord knownApp = mPidsSelfLocked.get(pid);
        if (knownApp != null && knownApp.thread == null) {
            // 若存在APP进程信息,但是IApplicationThread为空,则需要移除,并标记需要清理
            mPidsSelfLocked.remove(pid);
            gone = true;
        }
    }

    if (gone) {
        Slog.w(TAG, "Process " + app + " failed to attach");
        EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
                pid, app.uid, app.processName);
        removeProcessNameLocked(app.processName, app.uid);
        if (mHeavyWeightProcess == app) {
            mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
                    mHeavyWeightProcess.userId, 0));
            mHeavyWeightProcess = null;
        }
        mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
        // Take care of any launching providers waiting for this process.
        // 处理等待该进程启动的ContentProvider部分
        cleanupAppInLaunchingProvidersLocked(app, true);
        // Take care of any services that are waiting for the process.
        // 处理等待该进程启动的Service部分
        mServices.processStartTimedOutLocked(app);
        app.kill("start timeout", true);
        if (app.isolated) {
            mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
        }
        removeLruProcessLocked(app);
        if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
            Slog.w(TAG, "Unattached app died before backup, skipping");
            mHandler.post(new Runnable() {
            @Override
                public void run(){
                    try {
                        IBackupManager bm = IBackupManager.Stub.asInterface(
                                ServiceManager.getService(Context.BACKUP_SERVICE));
                        bm.agentDisconnected(app.info.packageName);
                    } catch (RemoteException e) {
                        // Can't happen; the backup manager is local
                    }
                }
            });
        }
        if (isPendingBroadcastProcessLocked(pid)) {
            Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
            // 跳过等待该进程启动的广播部分
            skipPendingBroadcastLocked(pid);
        }
    } else {
        // 否则仅打印一段日志
        Slog.w(TAG, "Spurious process start timeout - pid not known for " + app);
    }
}

该方法中会对AMS的mPidsSelfLocked成员中保存的ProcessRecord进行检查,若存在对应的ProcessRecord却不存在IApplicationThread,则需要进行清理工作。否则打印一段日志。
APP进程正常启动情况下,会将对应的ProcessRecord保存在mPidsSelfLocked中。

超时的取消

APP进程正常启动后,会通知AMS:
[ActivityThread.java]

private void attach(boolean system, long startSeq) {
    final IActivityManager mgr = ActivityManager.getService();
    try {
        // 通过IActivityManager binder通信接口通知到AMS,最终调用到AMS的attachApplication方法。
        // 这里将IApplicationThread和启动序列号传给AMS,用于AMS向APP进程通信和AMS匹配启动的ProcessRecord。
        mgr.attachApplication(mAppThread, startSeq);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

在AMS的attachApplication方法中又调用attachApplicationLocked方法:
[ActivityManagerService.java]

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    // ···
    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
    // ···
}

在AMS的attachApplicationLocked方法中将移除PROC_START_TIMEOUT_MSG消息。

Activity launch阶段

超时的设置

在ActivityStack的resumeTopActivityInnerLocked方法中,当判断APP进程存在时,将会调度Activity resume事务,并发送超时消息:

[ActivityStack.java]

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    // ···
    if (next.app != null && next.app.thread != null) {
        // next为将要显示的Activity对应的ActivityRecord
        next.completeResumeLocked();
    } else {
        // ··· 
    }
    // ···
}

在ActivityRecord的completeResumeLocked方法中将调用ActivityStackSupervisor的scheduleIdleTimeoutLocked方法

[ActivityStackSupervisor.java]

void scheduleIdleTimeoutLocked(ActivityRecord next) {
    if (DEBUG_IDLE) Slog.d(TAG_IDLE,
            "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
    // 发送IDLE_TIMEOUT_MSG消息
    Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
    // IDLE_TIMEOUT值为10s
    mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
}

通过mHandler(为ActivityStackSupervisorHandler)发送一个10sIDLE_TIMEOUT_MSG消息消息。

超时的触发

进入ActivityStackSupervisorHandler的handleMessage方法,看对应的IDLE_TIMEOUT_MSG case:
[ActivityStackSupervisor.java]

public void handleMessage(Message msg) {
    switch (msg.what) {
        case IDLE_TIMEOUT_MSG: {
            if (DEBUG_IDLE) Slog.d(TAG_IDLE,
                    "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
            // We don't at this point know if the activity is fullscreen,
            // so we need to be conservative and assume it isn't.
            // 调用ActivityStackSupervisorHandler的activityIdleInternal方法
            activityIdleInternal((ActivityRecord) msg.obj,
                    true /* processPausingActivities */);
        } break;
    }
}

最终将调用ActivityStackSupervisor的activityIdleInternalLocked方法。

超时的取消

Activity启动完成后,将调用AMS的activityIdle方法通知启动完成,在activityIdle中又会调用ActivityStackSupervisor的activityIdleInternalLocked方法:

[ActivityStackSupervisor.java]

final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
    boolean processPausingActivities, Configuration config) {
    ActivityRecord r = ActivityRecord.forTokenLocked(token);
    if (r != null) {
        // ···
        // 移除IDLE_TIMEOUT_MSG消息
        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
        // ···
    }

    if (allResumedActivitiesIdle()) {
        // ···

        if (mLaunchingActivity.isHeld()) {
            // 移除IDLE_TIMEOUT_MSG消息
            mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
            if (VALIDATE_WAKE_LOCK_CALLER &&
                    Binder.getCallingUid() != Process.myUid()) {
                throw new IllegalStateException("Calling must be system uid");
            }
            // 释放WakeLock
            mLaunchingActivity.release();
        }
        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    }
}

AMS在收到Activity启动完成通知调用后,就会移除IDLE_TIMEOUT_MSGLAUNCH_TIMEOUT_MSG消息。

Activity stop阶段

当目标Activity显示后,将处理待执行stop的那些Activity。

超时的设置

停止Activity会调用ActivityStack的stopActivityLocked方法:

[ActivityStack.java]

final void stopActivityLocked(ActivityRecord r) {
    // ···
    Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
    // STOP_TIMEOUT为 11 * 1000
    mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
    // ···
}

发送11s延迟的STOP_TIMEOUT_MSG消息。

超时的触发

进入ActivityStackHandler的handleMessage方法:

[ActivityStack.java]

public void handleMessage(Message msg) {
    switch (msg.what) {
        case STOP_TIMEOUT_MSG: {
            ActivityRecord r = (ActivityRecord)msg.obj;
            // We don't at this point know if the activity is fullscreen,
            // so we need to be conservative and assume it isn't.
            Slog.w(TAG, "Activity stop timeout for " + r);
            synchronized (mService) {
                if (r.isInHistory()) {
                    r.activityStoppedLocked(null /* icicle */,
                            null /* persistentState */, null /* description */);
                }
            }
        } break;
    }
}

即使APP进程端stop Activity超时,AMS也会进行后续的操作。

超时的取消

Activity在stop完成后,会调用AMS的activityStopped方法,在该方法中会查找对应ActivityRecord,调用其activityStoppedLocked方法:

[ActivityRecord.java]

final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
        CharSequence description) {
    // ···
    stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
    // ···
}

在该方法中移除STOP_TIMEOUT_MSG超时消息。

Activity destroy阶段

超时的设置

销毁APP进程中的Activity时会调用ActivityStack的destroyActivityLocked方法:

[ActivityStack.java]

final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
    r.setState(DESTROYING,
            "destroyActivityLocked. finishing and not skipping destroy");
    Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
    // DESTROY_TIMEOUT为 10 * 1000
    mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
}

发送10s延迟的DESTROY_TIMEOUT_MSG消息。

超时的触发

进入ActivityStackHandler的handleMessage方法:
[ActivityStack.java]

public void handleMessage(Message msg) {
    switch (msg.what) {
        case DESTROY_TIMEOUT_MSG: {
            ActivityRecord r = (ActivityRecord)msg.obj;
            // We don't at this point know if the activity is fullscreen,
            // so we need to be conservative and assume it isn't.
            Slog.w(TAG, "Activity destroy timeout for " + r);
            synchronized (mService) {
                // 进行清理和移除相关操作
                activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
            }
        } break;
    }
}

APP进程端destroy Activity如果超时,AMS会进行清理相关操作。

超时的取消

APP进程端destroy Activity完成后会调用AMS的activityDestroyed方法,该方法中最终会调用ActivityStack的activityDestroyedLocked方法:
[ActivityStack.java]

final void activityDestroyedLocked(ActivityRecord record, String reason) {
    if (record != null) {
        // 移除DESTROY_TIMEOUT_MSG消息
        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record);
    }
    // 进行清理和移除相关操作 ···
}

AMS收到通知后即会移除DESTROY_TIMEOUT_MSG消息。

总结

AMS在pause Activity时会通过Handler发送LAUNCH_TIMEOUT_MSG(10s)PAUSE_TIMEOUT_MSG(500ms)超时消息;在launch Application时发送**PROC_START_TIMEOUT_MSG(10s)超时消息;launch Activity时发送IDLE_TIMEOUT_MSG(10s)超时消息;stop Activity时发送STOP_TIMEOUT_MSG(11s)超时消息;destory Activity时发送DESTROY_TIMEOUT_MSG(10s)**超时消息。

当触发超时后,AMS将自动进行后续阶段的操作和清理操作。即使APP进程端处理耗时过久或异常或无响应,AMS也能保证调度过程不会因此出现停滞和混乱。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章