当前位置: 首页 > news >正文

Android源码分析 —— Activity栈管理(基于Android8)

0. 写在前面

本文基于 Android8.0源码,和Android9.0大同小异,但和Android10.0差别非常大!新版改用ATM来管理Activity的启动,Activity的生命周期也通过XXXItem来管理。由于我分析的Activity启动流程就是基于Android8/9的,所以本文仍然使用Android8源码来探索 Activity 栈管理。后续有时间再学习整理Android10.0的内容。

1. 前置知识

1.1 Activity栈管理相关类

ActivityStackSupervisor

Activity栈的管理人

ActivityDisplay

表示一个屏幕,Android支持三种屏幕,主屏幕,外接屏幕,虚拟屏幕(投屏)【这个介绍是从其他地方看来的,并不确定】。一般在手机上只有主屏幕,此时ActivityStackSupervisor与ActivityDisplay都是系统唯一的

TaskRecord

是ActivityTask的记录,TaskRecord是Activity栈的重要管理单元。形象一点理解,记得启动模式的 singleTask 吧?意思就是让这个Activity在单独的TaskRecord中启动。“Task":任务。

ActivityRecord

记录着每个Activity的信息,ActivityRecord和Activity一一对应。

ActivityStack

是ActivityRecord和TaskRecord两者的统一上司,记录着ActivityRecord和TaskRecord的状态。

如果在只有主屏幕的设备上,他们之间的关系大概是这样子的:

请添加图片描述

可以理解为一个屏幕上,可能会有很多个APP进程,每个APP进程对应一个ActivityStack,也就是activity栈,其中由于Activity的启动模式不同,又形成了若干个TaskRecord,其中包含着若干个ActivityRecord。

1.2 Activity 的四种启动模式,以及启动标识符

Standard

标准启动模式,启动Activity的时候向发起人的Task顶直接添加即可,返回时依次退出。

SingleTop

栈顶唯一,如果栈顶Activity不是要启动的Activity,则会创建一个新的Activity实例,但如果栈顶Activity就是我们要启动的Activity,就只会调用onNewIntent,而不去再重新创建一个实例。相比Standard,Standard不论如何,都会创建一个新的实例。

SingleTask

栈内唯一。如果发起启动的ActivityRecord所在的TaskRecord中,有要启动的Activity对应的ActivityRecord,则首先将TaskRecord中,目标Activity之上的所有ActivityRecord全都弹出,然后将所在TaskRecord变为ActivityStack的栈顶。

如果发起启动的ActivityRecord所在的TaskRecord中,没有要启动的Activity对应的ActivityRecord,则会在栈顶新建一个TaskRecord,并向其中实例化一个需要启动的Activity对应的ActivityRecord。

SingleInstance

独占一个TaskRecord。启动时,在ActivityStack中查找是否有相同的Activity,如果有,则用这个独占TaskRecord的ActivityRecord对应的Activity。否则新建一个TaskRecord,里面只有它存在。由singleInstance发起的启动,不论是谁,都会在另一个task中启动。

Activity的Flags标志位

  • FLAG_ACTIVITY_NEW_TASK,作用为指定 singleTask 启动模式
  • FLAG_ACTIVITY_SINGLE_TOP,作用为指定 singleTop 启动模式
  • FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK连用,主要用在复用Activity上,在TaskRecord中,复用的ActivityRecord之上的所有AR都要退出
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,具有这个标志位的Activity将不会出现在历史列表中。

2. 定位到AMS进行栈管理的代码

AMS接收到启动Activity的请求时,将实际Activity的启动任务分配给了ActivityStarter来处理,最后是通过ActivityStarter.startActivityUnchecked()与ActivityStackSupervisor合作完成的Activity栈管理。我们大概的来跟踪一遍这部分代码:

ActivityManagerService

binder通信告知ActivityManagerService需要启动activity。回顾binder通信的知识,需要注意的是 ActivityManagerService 是一个binder实体,是一个服务IActivityManager.Stub,它对外提供的方法都是由IActivityManager接口定义好的,所以我们能看到这些方法上加了 @Override 注解。AMS调用到了startActivity方法:

@Override
public final int startActivity(...) {return startActivityAsUser(...);
}
@Override
public final int startActivityAsUser(...) {//进入到了ActivityStarterreturn mActivityStarter.startActivityMayWait(...);
}

ActivityStarter

在startActivityMayWait()中,对intent做了一些处理,然后逐层调用重载的几个startActivity()方法:

//1.对intent做一些处理
final int startActivityMayWait(){startActivityLocked();
}
//2.继续往里调用
int startActivityLocked(){startActivity();
}
//3.做一些权限判断,在这里,new了一个ActivityRecord,为要启动的activity提前创造好了空壳ActivityRecord
int startActivity(){return startActivity();
}
//4.继续调用
int startActivity(){startActivityUnchecked();
}
//5.来到核心
int startActivityUncheckd(){//这会根据各种启动模式设定,进行activity的启动
}

在具体分析代码之前,我们来看一下startActivityUnchecked()最开头的 computeLaunchingTaskFlags(),看看启动模式标志位的设计,然后再分析后续代码。

private void computeLaunchingTaskFlags() { //mSourceRecord:当前Activity是被它发起启动的//mInTask:所在TaskRecord//mInTask.getStack()为这个TaskRecord所在的ActivityStackif (mSourceRecord == null && mInTask != null && mInTask.getStack() != null) {final Intent baseIntent = mInTask.getBaseIntent();final ActivityRecord root = mInTask.getRootActivity();//一些错误终止if (root == null) {//如果TaskRecord是空的final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK| FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)| (baseIntent.getFlags() & flagsOfInterest);//将flags整合起来,放到MIntent中mIntent.setFlags(mLaunchFlags);mInTask.setIntent(mStartActivity);mAddingToTask = true;} else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {//如果TaskRecord非空,而且标志位为 FLAG_ACTIVITY_NEW_TASK//那么新的ActivityRecord应当在新的TaskRecord中建立,所以mAddingToTask = falsemAddingToTask = false;} else {//如果要在当前的TaskRecord中建立,mAddingToTask = truemAddingToTask = true;}mReuseTask = mInTask;} else {mInTask = null;//如果是这是个起源任务,也就是最初的一个task。换句话说,这个activity是第一个activityif ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null&& mSourceRecord.isFreeform())  {mAddingToTask = true;}}//如果这个Activity就是第一个Activityif (mInTask == null) {if (mSourceRecord == null) {//这个activity不可能是被其他activity唤起的,例如app进程启动后的第一个activity。那么他的启动必然是在新的task中的。if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {Slog.w(TAG, "startActivity called from non-Activity context; forcing " +"Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;}} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//如果发起者的启动模式是singleInstance,那么由它启动的activity都应当在一个新的TaskRecord中mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;} else if (mLaunchSingleInstance || mLaunchSingleTask) {// 如果要启动的Activity的启动模式是singleInstance或者singleTask,且当前环境下是没有一个所在TaskRecord,那么就应当新建一个Task。mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;}}
}

我们再来看一下 startActivityUnchecked()中,getReusableIntentActivity()是如何获取一个可复用的Activity的:

private ActivityRecord getReusableIntentActivity() {// 新的ActivityRecord是否要放到现存的TaskRecord中呢?根据标志位做一些判断boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)|| mLaunchSingleInstance || mLaunchSingleTask;putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;//接下来就尝试获取一个目标ActivityActivityRecord intentActivity = null;if (mOptions != null && mOptions.getLaunchTaskId() != -1) {//指定一个TaskRecord,获取这个TaskRecord的顶部Activity来复用,可能是nullfinal TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());intentActivity = task != null ? task.getTopActivity() : null;} else if (putIntoExistingTask) {//如果没有指定,但是确实要放到一个现存的TaskRecord中,就需要看情况了:if (mLaunchSingleInstance) {//1.如果新Activity的启动模式为 singleInstance,那么就要在已存在的所有TaskRecord中,找找它是否存在,如果存在,则复用。(和singleInstance的基础介绍保持一致)intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,mStartActivity.isHomeActivity());} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {//2.如果启动标志位为:FLAG_ACTIVITY_LAUNCH_ADJACENT,这个比较特殊,是多窗口启动ActivityintentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,!mLaunchSingleTask);} else {// 否则,寻找一个合适的TaskRecord来放入intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId);}}return intentActivity;
}

3. 根据不同的启动模式,来分析代码逻辑

3.1 Standard

1. standard

普通启动模式,需要注意,如果是第一个Activity,它一定是new_task标志位,也就是即使在xml设置了standard,它也是new_task启动。那么如果一个Activity是以Standard模式启动的,它一定有mSourceRecord,即发起它启动的Activity。

在 startActivityUnchecked() 中一路跳过if()判断,来到这个方法的最后:

private int startActivityUnchecked(){computeLaunchingTaskFlags();//由于找不到一个合适的栈放入,reusedActivity = nullActivityRecord reusedActivity = getReusableIntentActivity();//...跳过了很多不通过的判断语句//1. 如果有可复用的Activity//2. 如果栈顶Activity就是自己//最终来到这里↓//到这里还是standard的启动模式,就一定有sourceActivity,而且这个sourceActivity一定不是singleInstance,否则在修正flag的时候,当前新的activity就被修正为new_task了if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {newTask = true;result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, preferredLaunchStackId, topStack);} else if (mSourceRecord != null) {//所以会进入到这里result = setTaskFromSourceRecord();} else if (mInTask != null) {result = setTaskFromInTask();} else {setTaskToCurrentTopOrCreateNewTask();}if (result != START_SUCCESS) {return result;}
}

我们只截取setTaskFromSourceRecord()核心代码来看:

private int setTaskFromSourceRecord(){//...//最后来到这里:// An existing activity is starting this new activity, so we want to keep the new one in// the same task as the one that is starting it.addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord");return START_SUCCESS;
}

然后进入 addOrReparentStartingActivity()

private void addOrReparentStartingActivity(TaskRecord parent, String reason) {if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {parent.addActivityToTop(mStartActivity);} else {//会进入到这里mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);}
}

最后执行到 TaskRecord.addActivityAtIndex(),最后将新ActivityRecord加到了TaskRecord的mActivities([ActivityRecord集合])中去:mActivities.add(index, r);

到此,standard启动模式,就完成了入栈操作。

2. standard + Intent.FLAG_ACTIVITY_CLEAR_TOP

如果不仅是standard启动模式,还增设了一个启动标识符 Intent.FLAG_ACTIVITY_CLEAR_TOP,如果原先TaskRecord中有它存在,就会进行 singleTask 的任务,具体在 singleTask中谈到实现。

3.2 singleTop

由于singleTop启动模式的Activity也不会得到 reusableIntentActivity,所以会跳过 startActivityUnchecked()的第一个if判断。但它将进入第二个逻辑块:如果当前的activity是栈顶元素,将会复用它。当然,如果栈顶是其他activity,那么当前新activity的启动模式将会和standard一样了。

我们来看一下栈顶就是这个activity的情况,仍然看到ActivityStarter的startActivityUnchecked():

// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
final ActivityStack topStack = mSupervisor.mFocusedStack;
final ActivityRecord topFocused = topStack.topActivity();
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
//如果当前栈顶activity就是自己的话,就dontStart不用再start了
final boolean dontStart = top != null && mStartActivity.resultTo == null&& top.realActivity.equals(mStartActivity.realActivity)&& top.userId == mStartActivity.userId&& top.app != null && top.app.thread != null&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0|| mLaunchSingleTop || mLaunchSingleTask);
if (dontStart) {//也就是可以直接复用if (mDoResume) {//首先让windowmanager.executeAppTransition()mSupervisor.resumeFocusedStackTopActivityLocked();}//然后直接分发onNewIntent事件deliverNewIntent(top);return START_DELIVERED_TO_TOP;
}

我们来看一下deliverNewIntent()是如何把消息发给activity的:

//ActivityStarter
private void deliverNewIntent(ActivityRecord activity) {if (mIntentDelivered) {return;}ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());//调用到ActivityRecord的deliverNewIntentLocked()activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,mStartActivity.launchedFromPackage);mIntentDelivered = true;
}//ActivityRecord
final void deliverNewIntentLocked(){//如果app正常app.thread.scheduleNewIntent(rintent,appToken,state==PAUSED);//如果app异常,将会把rintent记录,未来dump的时候可以获知if(unsent){addNewIntentLocked(rintent);}
}

看到这就很清晰了,就是通过binder通信,告知了app进程,现在需要处理onNewIntent任务。我们顺带简单看一下ActivityThread如何处理applicationThread这个binder实体接收到的newIntent通知:

//ApplicationThread in ActivityThread.java
public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token, boolean andPause) {NewIntentData data = new NewIntentData();data.intents = intents;data.token = token;data.andPause = andPause;sendMessage(H.NEW_INTENT, data);
}

接着由mH来handleNewIntent()

case NEW_INTENT:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");handleNewIntent((NewIntentData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;

handleNewIntent(NewIntentData data)->performNewIntents()

如果这个复用的activity还是resume状态,先让他onPause(),然后再给它发送newIntent,并通知其onResume()

void performNewIntents(IBinder token, List<ReferrerIntent> intents, boolean andPause) {final ActivityClientRecord r = mActivities.get(token);if (r == null) {return;}final boolean resumed = !r.paused;if (resumed) {//如果复用的activity目前还是resume状态,先让他onPausemInstrumentation.callActivityOnPause(r.activity);}deliverNewIntents(r, intents);if (resumed) {r.activity.performResume();r.activity.mTemporaryPause = false;}if (r.paused && andPause) {// In this case the activity was in the paused state when we delivered the intent,// to guarantee onResume gets called after onNewIntent we temporarily resume the// activity and pause again as the caller wanted.performResumeActivity(token, false, "performNewIntents");performPauseActivityIfNeeded(r, "performNewIntents");}
}

其中 deliverNewIntents() 回调了activity的onNewIntent()

private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) {final int N = intents.size();for (int i=0; i<N; i++) {ReferrerIntent intent = intents.get(i);intent.setExtrasClassLoader(r.activity.getClassLoader());intent.prepareToEnterProcess();//通知fragmentsr.activity.mFragments.noteStateNotSaved();//通知onNewIntentmInstrumentation.callActivityOnNewIntent(r.activity, intent);}
}

3.3 singleTask

“栈内唯一”的说法并不合适,实际上是TaskRecord内唯一。根据我画的这张图先来大概理解一下,再进入源码分析:

请添加图片描述

  1. 当前栈结构为最左情况,现在B_Activity要启动A_Activity,我们假定B_Activity的启动模式为standard,A_Activity的启动模式为singleTask
  2. 由于A_Activity的启动模式为singleTask,就需要尝试查找能否复用。
  3. 在ActivityStack中的所有TaskRecord中遍历查找是否存在A_Activity,发现存在于下面这个TaskRecord中。
  4. 先将该TaskRecord中,A_Activity之上其他的ActivityRecord全都弹出
  5. 然后将该TaskRecord浮动转移到所在ActivityStack的最顶端。

即使是C_Activity发起的启动A_Activity,只要ActivityStack中有一个TaskRecord存在A_Activity,就会对这个TaskRecord进行清除内部上方其他ActivityRecord的行为。流程图和上图一模一样,换一种解释,换一种解释说法:

  1. C_Activity的启动模式为 standard,A_Activity的启动模式为 singleTask
  2. 由于A_Activity的启动模式为singleTask,就需要尝试查找能否复用。
  3. 在ActivityStack中的所有TaskRecord中遍历查找是否存在A_Activity,发现存在于下面这个TaskRecord中。
  4. 先将该TaskRecord中,A_Activity之上其他的ActivityRecord全都弹出
  5. 然后将该TaskRecord浮动转移到所在ActivityStack的最顶端。
  6. C_Activity所在的TaskRecord并没有被移除,仅仅是被转移到了ActivityStack的下面而已。

继续分析源码,我们又来到了ActivityStarter的startActivityUnchecked(),首先尝试getReusableIntentActivity() 获取可复用的Activity:

private ActivityRecord getReusableIntentActivity(){boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)|| mLaunchSingleInstance || mLaunchSingleTask;//true//...else{intentActivity = mSupervisor.findTaskLocked();}return intentActivity;
}

mSupervisor.findTaskLocked()将会遍历所有的TaskRecord,查看是否有目标activity,如果有,则作为intentActivity返回出去,如果没有就是null。这两个情况都需要我们讨论。先来讨论一下有TaskRecord存在这个ActivityRecord的情况:

private int startActivityUnchecked(){//...ActivityRecord reusedActivity = getReusableIntentActivity();//由于找到了一个可复用的activity,进入到下面这个判断体内if(reusedActivity != null){//由于singleTask启动模式,所以会进到下面:if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0|| isDocumentLaunchesIntoExisting(mLaunchFlags)|| mLaunchSingleInstance || mLaunchSingleTask) {//拿到所在的TaskRecordfinal TaskRecord task = reusedActivity.getTask();//将TaskRecord中在它之上的所有ActivityRecord全都移除final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,mLaunchFlags);if (top != null) {if (top.frontOfTask) {top.getTask().setIntent(mStartActivity);}deliverNewIntent(top);}}//之后,可能需要将这个TaskRecord转移到ActivityStack的顶部,也就是栈顶:内部调整位置的逻辑就不进去看了reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);}
}

接下来的逻辑就是发送newIntent到app进程,如果这个activity之前是pause状态的话,还会performResumeActivity(),在此之前会先行回调onNewIntent()

3.3 singleInstance

它不仅在ActivityStack栈内唯一,而且还独占一个TaskRecord。可以理解为它位分最高。它的代码逻辑很简单了,结合singleTask来看就好了。其他需要注意的地方是,如果由它启动任何一个activity,都将被设置为 flag_activity_new_task模式:

我们又回到ActivityStarter中,看到startActivityUnchecked()的computeLaunchingTaskFlags(),我们只关注singleInstance作为源sourceRecord启动别的activity的情况:

private void computeLaunchingTaskFlags() {//...if (mInTask == null) {if (mSourceRecord == null) {//...} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//有源,而且源是 singleInstance 的启动模式//那么不论你之前的flag是什么样的,都会被标记上 flag_activity_new_task!!mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;} else if (mLaunchSingleInstance || mLaunchSingleTask) {mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;}}
}

4. 讨论一下进程第一个Activity在这里的启动是如何表现的

其实我们之前也看了,如果没有源头mSourceRecord,也没有mInTask,这就是第一个activity,那么它的启动标识符就一定会被加上 FLAG_ACTIVITY_NEW_TASK。也就是说,他会是这个APP进程的ActivityStack的第一个TaskRecord的第一个ActivityRecord。

说到这里,我们还需要将它和之前Activity启动流程的步骤做一个整合,我们来分析一下,

我们不应该聚焦在AMS通知application初始化,而应该再往前看一点,从点击桌面图标,到打开APP进程。再点击桌面图标的时候,其实就是startActivity(),这个我们之前讨论的很全面了:

  1. AMS接收到startActivity()的通知,将任务交给了ActivityStarter

  2. ActivityStarter在不断调用startActivity()的过程中,为要启动的Activity创建了一个空壳ActivityRecord

  3. 最后ActivityStarter来到了startActivityUnchecked()由于这个ActivityRecord还没有任何绑定,所以最后进入到了ActivityStackSupervisor.resumeFocusedStackTopActivityLocked()

    boolean resumeFocusedStackTopActivityLocked(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {if (!readyToResume()) {return false;}//进入到resumeTopActivityUncheckedLocked()if (targetStack != null && isFocusedStack(targetStack)) {return //然后进入到resumeTopActivityInnerLocked()这部分代码特别长,就不贴了,在这里面会调用startSpecificActivityLocked()targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);}return false;
    }
    
  4. ActivityStackSupervisor调用到startSpecificActivityLocked(),会进行一个检查,看看这个activity的app进程是否运行了:

    void startSpecificActivityLocked(ActivityRecord r,boolean andResume, boolean checkConfig) {// Is this activity's application already running?ProcessRecord app = mService.getProcessRecordLocked(r.processName,r.info.applicationInfo.uid, true);r.getStack().setLaunchTime(r);//如果这个ActivityRecord的进程已经在运行了,进入这里if (app != null && app.thread != null) {}//如果没有,则先启动进程~//通过AMS的startProcessLocked()来启动进程mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,"activity", r.intent.getComponent(), false, false, true);
    }
    
  5. AMS.startProcessLocked()中间会调用到 Process.start()

    private final void startProcessLocked(...){//...startResult = Process.start(...);
    }
    
  6. 然后就是我们熟悉的zygote.fork()的任务了

然后。我们需要聚焦到AMS在通知完application初始化之后,又通知了ActivityStackSupervisor.attachApplicationLocked(),在这里面进行了栈管理。我们来到ActivityStackSupervisor:

//ActivityStackSupervisor(栈管理者)
//这里的ProcessRecord中管理着整个进程的各种信息,包括运行中的ActivityRecord,以及ServiceRecord、ConnectionRecord、ReceiverList、ContentProvider等各种信息
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {final String processName = app.processName;boolean didSomething = false;//遍历所有屏幕,我们之前谈过安卓设备可能有主屏幕、外界屏幕、虚拟屏幕,一般情况下手机就只有主屏幕。还可能有分屏。for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {//拿到一个屏幕下,所有ActivityStackArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {final ActivityStack stack = stacks.get(stackNdx);//找到当前显示的/持有焦点的ActivityStack//这个activitystack的任务是接收用户输入,或者启动另一个activityif (!isFocusedStack(stack)) {continue;}//获取所有正在运行且可见的activity,放到mTmpActivityList中stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);//获取正在运行的Activity中的最顶端ActivityRecordfinal ActivityRecord top = stack.topRunningActivityLocked();final int size = mTmpActivityList.size();for (int i = 0; i < size; i++) {final ActivityRecord activity = mTmpActivityList.get(i);//这个activity的processRecord为空,但它的processName以及有了,这是app进程初始在某个时候注册的ActivityRecordif (activity.app == null && app.uid == activity.info.applicationInfo.uid&& processName.equals(activity.processName)) {//判断活动的activity的进程是不是和我要启动的activity的进程一致,如果是,就进入到这里面来try {//开启activityif (realStartActivityLocked(activity, app,top == activity /* andResume */, true /* checkConfig */)) {didSomething = true;}} catch (RemoteException e) {Slog.w(TAG, "Exception in new application when starting activity "+ top.intent.getComponent().flattenToShortString(), e);throw e;}}}}}if (!didSomething) {ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);}return didSomething;
}

当前APP的ActivityStack有一个空的ActivityRecord,需要realStartActivityLocked()来真正启动ActivityRecord对应的activity。它这个时候还是个空壳。

进入到realStartActvitiyLocked():

//ActviityStackSupervisor
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,boolean andResume, boolean checkConfig){//这个taskrecord目前只有一个空壳activityRecordfinal TaskRecord task = r.getTask();//这个activityStack目前也只有一个TaskRecord和一个空壳activityrecordfinal ActivityStack stack = task.getStack();r.app = app;//为这个ActivityRecord绑定ProcessRecordint idx = app.activities.indexOf(r);//为ProcessRecord绑定activityRecordif (idx < 0) {app.activities.add(r);}//binder通信,通知app进程,可以启动activity。注意,这里的r是ActivityRecord,这是个空壳,但是到这里,它的意义就是APP进程的第一个activity。app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,...);}

至此,activity启动完成。

总结

从一个LauncherActivity到APP进程的activity,Activity的栈,在其中的工作大致如下:

  1. Activity的启动一般可以通过startActivity()来进行,通过Instrumentation.execStartActivity(),最终会通知到AMS来进行Activity的启动。
  2. AMS通过binder线程获知了需要启动Activity的任务,让ActivityStarter去完成activity的启动。
  3. ActivityStarter在一连串的startActivity()调用过程中,为要启动的Activity创建了一个ActivityRecord。
  4. 最后进入到startActivityUnchecked(),根据Activity的启动模式与启动标识符的不同进行不同的处理。
  5. 如果这个Activity是新进程的Activity,将会通知AMS先进行APP进程的启动,APP进程的application启动完成后,会通知AMS,application初始完成,并将APP进程的binder代理交给AMS,AMS再通过ActivityStartSupervisor来realStartActivityLocked()->app.thread.scheduleLaunchActivity()来通知APP进程,可以启动activity了。
  6. 如果这个Activity是本进程发起的启动,那么就会根据发起者Activity的启动模式以及新Activity的启动模式综合判断,是复用Activity接着调用newIntent()呢,还是新建一个Activity,然后也进入到realStartActivityLocked()->app.thread.scheduleLaunchActivity()来启动新的Activity.

相关文章:

Android源码分析 —— Activity栈管理(基于Android8)

0. 写在前面 本文基于 Android8.0源码&#xff0c;和Android9.0大同小异&#xff0c;但和Android10.0差别非常大&#xff01;新版改用ATM来管理Activity的启动&#xff0c;Activity的生命周期也通过XXXItem来管理。由于我分析的Activity启动流程就是基于Android8/9的&#xff…...

Python实现贝叶斯优化器(Bayes_opt)优化支持向量机分类模型(SVC算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。1.项目背景贝叶斯优化器(BayesianOptimization) 是一种黑盒子优化器&#xff0c;用来寻找最优参数。贝叶斯优化器是基…...

【华为OD机试模拟题】用 C++ 实现 - 分积木(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...

FFmpeg/OpenCV 实现全屏斜体水印

实现思路 &#x1f914;​ 基于ffmpeg&#xff0c;画布的方式&#xff0c;创建画布 -> 水印 -> 旋转 -> 抠图 -> 叠加到图像上基于ffmpeg&#xff0c;旋转图片的方式&#xff0c;填充 -> 水印 -> 顺时针旋转 -> 逆时针旋转 -> 截图基于opencv&#xff…...

Calendar计算两个时间之间相差几个月

目录说明说明 计算两个时间之间相差几个月&#xff1a; public int getMonth(String startDt, String endDt) { int month 0;try {SimpleDateFormat sdf new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Calendar satrt Calendar.getInstance();Calendar end Cal…...

FPGA基础知识

FPGA是在PAL、PLA和CPLD等可编程器件的基础上进一步发展起来的一种更复杂的可编程逻辑器件。它是ASIC领域中的一种半定制电路&#xff0c;既解决了定制电路的不足&#xff0c;又克服了原有可编程器件门电路有限的缺点。 由于FPGA需要被反复烧写&#xff0c;它实现组合逻辑的基…...

C语言运算符逻辑运算符位运算符

逻辑运算符 下表显示了 C 语言支持的所有关系逻辑运算符。假设变量 A 的值为 1&#xff0c;变量 B 的值为 0&#xff0c;则&#xff1a; 运算符 描述 实例 && 称为逻辑与运算符。如果两个操作数都非零&#xff0c;则条件为真。 (A && B) 为假。 || 称为逻辑…...

机器学习:基于主成分分析(PCA)对数据降维

机器学习&#xff1a;基于主成分分析&#xff08;PCA&#xff09;对数据降维 作者&#xff1a;AOAIYI 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;AOAIYI首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可…...

软件测试之测试模型

软件测试的发展 1960年代是调试时期&#xff08;测试即调试&#xff09; 1960年 - 1978年 论证时期&#xff08;软件测试是验证软件是正确的&#xff09;和 1979年 - 1982年 破坏性测试时期&#xff08;为了发现错误而执行程序的过程&#xff09; 1983年起&#xff0c;软件测…...

【项目精选】网络考试系统的设计与实现(源码+视频+论文)

点击下载源码 网络考试系统主要用于实现高校在线考试&#xff0c;基本功能包括&#xff1a;自动组卷、试卷发布、试卷批阅、试卷成绩统计等。本系统结构如下&#xff1a; &#xff08;1&#xff09;学生端&#xff1a; 登录模块&#xff1a;登录功能&#xff1b; 网络考试模块…...

Python近红外光谱分析与机器学习、深度学习方法融合实践技术

、 第一n入门基础【理论讲解与案 1、Python环境搭建&#xff08; 下载、安装与版本选择&#xff09;。 2、如何选择Python编辑器&#xff1f;&#xff08;IDLE、Notepad、PyCharm、Jupyter…&#xff09; 3、Python基础&#xff08;数据类型和变量、字符串和编码、list和tu…...

实例7:树莓派呼吸灯

实例7&#xff1a;树莓派呼吸灯 实验目的 通过背景知识学习&#xff0c;了解digital与analog的区别。通过GPIO对外部LED灯进行呼吸控制&#xff0c;熟悉PWM技术。 实验要求 通过python编程&#xff0c;用GPIO控制LED灯&#xff0c;使之亮度逐渐增大&#xff0c;随后减小&am…...

java 接口 详解

目录 一、概述 1.介绍 : 2.定义 : 二、特点 1.接口成员变量的特点 : 2.接口成员方法的特点 : 3.接口构造方法的特点 : 4.接口创建对象的特点 : 5.接口继承关系的特点 : 三、应用 : 1.情景 : 2.多态 : ①多态的传递性 : ②关于接口的多态参数和多态…...

用 Visual Studio 升级 .NET 项目

现在&#xff0c;你已可以使用 Visual Studio 将所有 .NET 应用程序升级到最新版本的 .NET&#xff01;这一功能可以从 Visual Studio 扩展包中获取&#xff0c;它会升级你的 .NET Framework 或 .NET Core 网页和桌面应用程序。一些项目类型仍正在开发中并将在不久的未来推出&a…...

JavaWeb中FilterListener的神奇作用

文章目录1&#xff0c;Filter1.1 Filter概述1.2 Filter快速入门1.2.1 开发步骤1.3 Filter执行流程1.4 Filter拦截路径配置1.5 过滤器链1.5.1 概述1.5.2 代码演示1.5.3 问题2&#xff0c;Listener2.1 概述2.2 分类2.3 代码演示最后说一句1&#xff0c;Filter 1.1 Filter概述 F…...

移动端布局

参考链接&#xff1a;抖音-移动端适配 一、移动端布局 flexiblepostcss-pxtorem vue-h5-template 老版本&#xff1a;动态去计算scale&#xff0c;并不影响rem的计算&#xff0c;好处是解决了1px的问题&#xff0c;但是第三方库一般都用dpr为1去做的&#xff0c;这就导致地图或…...

前端无感登录,大文件上传

后端设置token的一个失效时间&#xff0c;前端在token失效后不用重新登录 1&#xff0c;在相应中拦截&#xff0c;判断token返回过期后&#xff0c;调用刷新token的方法 2&#xff0c;后端返回过期的时间&#xff0c;前端判断过期的时间&#xff0c;然后到期后调用对应的方法…...

Spring Boot系列03--自动配置原理

目录1. 相关注解2. 自动配置原理分析3. 自动配置图示Spring Boot的核心优势&#xff1a;自动装配、约定大于配置。 1. 相关注解 ConfigurationProperties(prefix "前缀名")该注解用于自动配置的绑定&#xff0c;可以将application.properties配置中的值注入到 Bean…...

Java多线程(四)---并发编程容器

1.经常使用什么并发容器&#xff0c;为什么&#xff1f;答&#xff1a;Vector、ConcurrentHashMap、HasTable一般软件开发中容器用的最多的就是HashMap、ArrayList&#xff0c;LinkedList &#xff0c;等等但是在多线程开发中就不能乱用容器&#xff0c;如果使用了未加锁&#…...

Apache Hadoop生态部署-Flume采集节点安装

目录 Apache Hadoop生态-目录汇总-持续更新 一&#xff1a;安装包准备 二&#xff1a;安装与常用配置 2.1&#xff1a;下载解压安装包 2.2&#xff1a;解决guava版本问题 2.3&#xff1a;修改配置 三&#xff1a;修复Taildir问题 3.1&#xff1a;Taildir Source能断点续…...

【OpenFOAM】-算例解析合集

【OpenFOAM】-算例解析合集OlaFlowinterFoamOlaFlow 【OpenFOAM】-olaFlow-算例1- baseWaveFlume 【OpenFOAM】-olaFlow-算例2- breakwater 【OpenFOAM】-olaFlow-算例3- currentWaveFlume 【OpenFOAM】-olaFlow-算例4- irreg45degTank 【OpenFOAM】-olaFlow-算例5- oppositeS…...

数据库|(一)数据库和SQL概述

&#xff08;一&#xff09;数据库和SQL概述1.1 数据库的好处1.2 数据库的概念1.3 数据库结构特点1.1 数据库的好处 实现数据持久化使用完整的管理系统统一管理&#xff0c;便于查询 1.2 数据库的概念 DB 数据库&#xff08;database&#xff09;&#xff0c;存储数据的仓库&…...

【java基础】自定义类

文章目录基本介绍自定义类字段方法构造器main方法基本介绍 什么是类这里就不过多赘述了&#xff0c;这里来介绍关于类的几个名词 类是构造对象的模板或蓝图由类构造对象的过程称为创建类的实例封装就是将数据和行为组合在一个包中&#xff0c;并对对象的使用者隐藏具体的实现…...

7、STM32 FSMC驱动SRAM

本次使用CubeMx配置FSMC驱动SRAM,XM8A51216 IS62WV51216 原理图&#xff1a; 注意&#xff1a;FSMC_A0必须对应外部设备A0引脚 一、FSMC和FMC区别 FSMC&#xff1a;灵活的静态存储控制器 FMC:灵活存储控制器 区别&#xff1a;FSMC只能驱动静态存储控制器&#xff08;如&…...

七、虚拟机栈

虚拟机栈出现的背景 1.由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的&#xff0c;不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。 2.优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同…...

Linux其他常用命令

Linux其他常用命令查找文件find 命令功能非常强大&#xff0c;通常用在特定目录下搜索符合条件的文件如果省略路径&#xff0c;表示在当前文件夹下查找之前学习的通配符&#xff0c;在使用 find 命令时同时可用演练目标1.搜索桌面目录下&#xff0c;文件名包含1的文件find Desk…...

一次性打包学透 Spring

不知从何时开始&#xff0c;Spring 这个词开始频繁地出现在 Java 服务端开发者的日常工作中&#xff0c;很多 Java 开发者从工作的第一天开始就在使用 Spring Framework&#xff0c;甚至有人调侃“不会 Spring 都不好意思自称是个 Java 开发者”。 之所以出现这种局面&#xf…...

1080T、2080T、4070T显卡的深度学习性能测试和结论

先说结论&#xff1a; 4070T显卡FP32的训练和推理速度跟3090应该基本类似。但由于显存12G偏低&#xff0c;4070T不太适合如今的深度学习模型训练&#xff08;新手列外&#xff0c;大部分模型都能训练起来&#xff0c;耗电也相对很低&#xff09;&#xff0c;更适合测试最新的一…...

SpringBoot搭建SpringMVC项目

前言据我的了解&#xff0c;现在不管是大公司或是小公司&#xff0c;如果使用java开发一个web项目&#xff0c;大部分都会选择使用SpringBoot&#xff0c;关于Springboot的好处&#xff0c;就不在这里过多赘述&#xff0c;总之Springboot有一套完整的生态&#xff0c;从项目构建…...

Prescriptive Analytics for Flexible Capacity Management

3 本节根据Netessine等人&#xff08;2002年&#xff09;和Bassok等人&#xff08;1999年&#xff09;对我们解决的容量规划问题进行了正式描述。考虑一家以pi&#xff08;I1&#xff0c;…&#xff0c;I&#xff09;的单价提供I服务的公司。在每个计划周期t∈{1&#xff0c;……...