Android Framework- AMS 之 Activity-暂停

今天在和哥们闲聊,聊到之前看的启动页面的流程,哥们突然问了一句。 在新的页面启动之前,老的页面做了什么操作,你知道么?

我抓紧看看吧,省的别人问我,装X失败咋办。

ActivityStarter.startActivityInner()开始

java 复制代码
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;

//省略....
if (mTargetRootTask == null) {
    mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
            mOptions);
}
if (newTask) {
    final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
            ? mSourceRecord.getTask() : null;
    setNewTask(taskToAffiliate);
}


mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
        mOptions, sourceRecord);

computeTargetTask()的作用是匹配看看启动的页面有没有可以塞的Task。一般是null,这个和启动模式有关。 getOrCreateRootTask()的作用是构建了一个Task挂载到了taskDisplayArea上。 也就是 DefaultTaskDisplayArea 下。

mTargetRootTask.startActivityLocked()里 调用显示启动窗口。

ini 复制代码
mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask,
        isTaskSwitch, sourceRecord);
java 复制代码
//RootWindowContainer.getOrCreateRootTask();

//省略...
if (taskDisplayArea == null) {
    taskDisplayArea = getDefaultTaskDisplayArea();
}
return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
        launchParams, launchFlags, activityType, onTop);

继续下看

java 复制代码
mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
        mOptions, sourceRecord);
          

这部分就是将要启动的ActivityRecord放到Task的最上面,然后启动窗口动画。 到这里为止 就是构建Task 挂载到DefaultTaskDisplayArea 下,并将我们要启动的ActivityRecord 放到Task 最上层,启动窗口动画。

继续看

java 复制代码
mRootWindowContainer.resumeFocusedTasksTopActivities(
        mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);


--> targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions,
                    deferPause);
-->resumeTopActivityInnerLocked(prev, options, deferPause);
-->
topFragment.resumeTopActivity(prev, options, deferPause);
-->taskDisplayArea.pauseBackTasks(next);
//TaskFragment 注意这里参数为false 和resuming不为null
-->leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")
-->schedulePauseActivity()
-->completePause()

TaskFragment.completePause()主要就是设置 ActivityRecord设置state PAUSED

java 复制代码
if (prev != null) {
    prev.setWillCloseOrEnterPip(false);
    final boolean wasStopping = prev.isState(STOPPING);
    //ActivityRecord设置state 为PAUSED 
    prev.setState(PAUSED, "completePausedLocked");    

这里会导致页面的生命周期发生变化。 会进入 PauseActivityItem的逻辑。

java 复制代码
//PauseActivityItem
@Override
public void execute(ClientTransactionHandler client, ActivityClientRecord r,
        PendingTransactionActions pendingActions) {
  
    client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, mAutoEnteringPip,
            pendingActions, "PAUSE_ACTIVITY_ITEM");
   
}


@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
        PendingTransactionActions pendingActions) {
    if (mDontReport) {
        return;
    }
   
    ActivityClient.getInstance().activityPaused(token);
}

execute() 回调Activity onPause() postExecute() 会回调到 ActivityClientControlleractivityPaused()

java 复制代码
//ActivityClientController 
@Override
public void activityPaused(IBinder token) {
    final long origId = Binder.clearCallingIdentity();
    synchronized (mGlobalLock) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
        if (r != null) {
            r.activityPaused(false);
        }
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
    Binder.restoreCallingIdentity(origId);
}

这里会调用到 ActivityRecordactivityPaused()

java 复制代码
//ActivityRecord
void activityPaused(boolean timeout) {
  

    final TaskFragment taskFragment = getTaskFragment();
    if (taskFragment != null) {
    //移除超时
        removePauseTimeout();

        final ActivityRecord pausingActivity = taskFragment.getPausingActivity();
        if (pausingActivity == this) {

            mAtmService.deferWindowLayout();
            try {
                taskFragment.completePause(true /* resumeNext */, null /* resumingActivity */);
            } finally {
                mAtmService.continueWindowLayout();
            }
            return;
        } 
        //省略...
    }

    mDisplayContent.handleActivitySizeCompatModeIfNeeded(this);
    mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}

这里又进入了taskFragment.completePause() 但是这里参数分别是truenull

java 复制代码
//TaskFragment
void completePause(boolean resumeNext, ActivityRecord resuming) {
  
    ActivityRecord prev = mPausingActivity;
  

    if (prev != null) {
        prev.setWillCloseOrEnterPip(false);
        final boolean wasStopping = prev.isState(STOPPING);
        prev.setState(PAUSED, "completePausedLocked");
        if (prev.finishing) {
           
            prev = prev.completeFinishing(false /* updateVisibility */,
                    "completePausedLocked");
        } else if (prev.hasProcess()) {
          
            if (prev.deferRelaunchUntilPaused) {
               
               
                prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
            } else if (wasStopping) {
               
                prev.setState(STOPPING, "completePausedLocked");
            } else if (!prev.isVisibleRequested() || shouldSleepOrShutDownActivities()) {
              
                prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
                        "completePauseLocked");
            }
        } else {
            ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
            prev = null;
        }
        
        if (prev != null) {
            prev.stopFreezingScreenLocked(true /*force*/);
        }
        mPausingActivity = null;
    }

//这里主要是找到新启动的页面,并进入onResume状态
    if (resumeNext) {
        final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
        if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
            mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev,
                    null /* targetOptions */);
        } else {
            // checkReadyForSleep();
            final ActivityRecord top =
                    topRootTask != null ? topRootTask.topRunningActivity() : null;
            if (top == null || (prev != null && top != prev)) {
                
                mRootWindowContainer.resumeFocusedTasksTopActivities();
            }
        }
    }

    if (prev != null) {
        prev.resumeKeyDispatchingLocked();
    }

    mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);


    if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
            || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
        mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
        mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
    }
}

简单理解,就是在老页面onPause()以后 会去检查新的页面是否已经准备好,如果没有准备好则等待新的页面符合条件后进行。 如果新的页面此刻没有完成构建。 在 realStartActivityLocked() 方法中,会判断老的页面是否都已经onPause() ,才去构建新的页面,页面构建完成后通过调用 rootTask.minimalResumeActivityLocked(r)将状态推进到onResume()

java 复制代码
//ActivityTaskSupervisor
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
        boolean andResume, boolean checkConfig) throws RemoteException {

    if (!mRootWindowContainer.allPausedActivitiesComplete()) {
    
      
        return false;
    }
    
    
    //省略...
    
if (andResume && readyToResume()) {
     //执行onResume
    rootTask.minimalResumeActivityLocked(r);
}
    

总结一下:

  • 新的页面构建的时候,会构建新的Task 挂载到DefaultTaskDisplayArea下, 会将新构建的ActivityRecord 放置到Task的最上层,并启动启动窗口。
  • 新的Activity在构建之前,会判断所有老的页面是否进入了隐藏。如果符合条件会进入构建流程,并在构建完成后推进到onResume()
  • 老的页面会通过ActivityRecord的setState(PAUSED)的形式,进入PauseActivityItem的逻辑,在完成自身onPause()后,会检查新的页面是否可以启动。
  • Application的构建不受到老页面onPause()的限制,但是在走到realStartActivityLocked(),执行生命周期的时候,会去进行判断。
相关推荐
s***11701 天前
Mysql convert函数、convert用法、字符串转数字、字符串转日期、类型转换函数
android·数据库·mysql
n***26561 天前
【MySQL】MVCC详解, 图文并茂简单易懂
android·数据库·mysql
程序猿陌名!1 天前
Android-EDLA RK3576谷歌ATTESTION-KEY从申请到烧录以及验证谷歌认证标志全流程
android
安卓理事人1 天前
安卓版本升级功能
android
s***35301 天前
怎么下载安装yarn
android·前端·后端
z***94841 天前
使用rustDesk搭建私有远程桌面
android·前端·后端
q***06291 天前
【细如狗】记录一次使用MySQL的Binlog进行数据回滚的完整流程
android·数据库·mysql
0***86331 天前
图文详述:MySQL的下载、安装、配置、使用
android·mysql·adb
9***44631 天前
SQLyog安装配置(注册码)连接MySQL
android·mysql·adb
o***11141 天前
【MySQL】MySQL库的操作
android·数据库·mysql