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

news2024/11/18 13:27:46

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(...) {
    //进入到了ActivityStarter
    return 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所在的ActivityStack
    if (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 = false
            mAddingToTask = false;

        } else {
            //如果要在当前的TaskRecord中建立,mAddingToTask = true
            mAddingToTask = true;
        }
        mReuseTask = mInTask;
    } else {
        mInTask = null;
        //如果是这是个起源任务,也就是最初的一个task。换句话说,这个activity是第一个activity
        if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
            && mSourceRecord.isFreeform())  {
            mAddingToTask = true;
        }
    }
	//如果这个Activity就是第一个Activity
    if (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;
    //接下来就尝试获取一个目标Activity
    ActivityRecord intentActivity = null;
    if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
        //指定一个TaskRecord,获取这个TaskRecord的顶部Activity来复用,可能是null
        final 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,这个比较特殊,是多窗口启动Activity
            intentActivity = 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 = null
    ActivityRecord 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状态,先让他onPause
        mInstrumentation.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();
        //通知fragments
        r.activity.mFragments.noteStateNotSaved();
        //通知onNewIntent
        mInstrumentation.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) {
            //拿到所在的TaskRecord
            final 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) {
        //拿到一个屏幕下,所有ActivityStack
        ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
            final ActivityStack stack = stacks.get(stackNdx);
            //找到当前显示的/持有焦点的ActivityStack
            //这个activitystack的任务是接收用户输入,或者启动另一个activity
            if (!isFocusedStack(stack)) {
                continue;
            }
            //获取所有正在运行且可见的activity,放到mTmpActivityList中
            stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
            //获取正在运行的Activity中的最顶端ActivityRecord
            final 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进程初始在某个时候注册的ActivityRecord
                if (activity.app == null && app.uid == activity.info.applicationInfo.uid
                    && processName.equals(activity.processName)) {
                    //判断活动的activity的进程是不是和我要启动的activity的进程一致,如果是,就进入到这里面来
                    try {
                        //开启activity
                        if (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目前只有一个空壳activityRecord
    final TaskRecord task = r.getTask();
    //这个activityStack目前也只有一个TaskRecord和一个空壳activityrecord
    final ActivityStack stack = task.getStack();
    
    r.app = app;//为这个ActivityRecord绑定ProcessRecord
    int idx = app.activities.indexOf(r);//为ProcessRecord绑定activityRecord
    if (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.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370435.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg/OpenCV 实现全屏斜体水印

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

Pag渲染过程 -- 背景知识

什么是渲染 渲染是图形程序的核心&#xff0c;无论是我们在电子设备上看到的任何图形或者文字都是利用计算机图形渲染技术给我们呈现出来的结果。在计算机里一开始是直接利用CPU往显示器的FrameBuffer内写入数据即可把图形展示到显示器上&#xff0c;但是随着用户的需求和技术…

别担心,ChatGPT还抢不动你的饭碗

前言&#xff1a; “你是谁&#xff1f;” “我是一个由OpenAI训练的大型语言模型。我旨在帮助人们解答问题和提供信息。由于我是一个计算机程序&#xff0c;所以不会感知或者思考&#xff0c;只能通过已有的数据来回答问题。如果您有任何问题&#xff0c;请随时告诉我。” ---…

rabbitmq部署安装(mac)

安装&#xff1a; // 默认已经下载了homebrew&#xff0c;更新brew资源 brew update // 执行安装 brew install rabbitmq 配置&#xff1a; // 切换到MQ目录,注意你的安装版本可能不是3.9.5&#xff08;我的版本&#xff0c;当前最新版 cd /usr/local/Cellar/rabbitmq/3.…

如果不是互联网人,谁会找到这些神器?

一、上线啦 你肯定该问了&#xff0c;这个是什么鬼东西。它本来是一个创建自己网站的网站。 现在使用它可以创建自己的小程序&#xff0c;又不是有点小厉害了。 而且功能强大&#xff0c;还支持微信支付&#xff0c;分销&#xff0c;优惠券&#xff0c;营销等多种功能。 还有多…

DS期末复习卷(九)

一、选择题(30分) 1&#xff0e;下列程序段的时间复杂度为&#xff08;A &#xff09;。 for(i0&#xff1b; i<m&#xff1b; i) for(j0&#xff1b; j<t&#xff1b; j) c[i][j]0&#xff1b; for(i0&#xff1b; i<m&#xff1b; i) for(j0&#xff1b; j<t&am…

LeetCode:二叉树的最大深度104;559. N 叉树的最大深度

104. 二叉树的最大深度 给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 示例&#xff1a; 给定二叉树 [3,9,20,null,null,15,7]&#xff0c; 3/ \9 20/ \15 7 返回它的…

看似平平无奇的00后,居然一跃上岸字节,表示真的卷不过......

又到了一年一度的求职旺季金&#xff01;三&#xff01;银&#xff01;四&#xff01;在找工作的时候都必须要经历面试这个环节。在这里我想分享一下自己上岸字节的面试经验&#xff0c;过程还挺曲折的&#xff0c;但是还好成功上岸了。大家可以参考一下&#xff01; 0821测评 …

变则通--通则达--MindSpore社区活动-深度学习模型之数据变换-Transforms学习与体验记录

文章目录已加入 昇思MindSpore社区 证明截图安装 MindSpore 2.0 alpha 步骤打开powershell 或 cmd安装成功的截图实现 数据变换Transforms 任务Common Transforms 通用数据变换Vision Transforms 针对图像数据的变换Text Transforms 文本数据的变换Lambda Transforms结语已加入…

Netty——序列化的作用及自定义协议

序列化的作用及自定义协议序列化的重要性大小对比效率对比自定义协议序列化数据结构自定义编码器自定义解码器安全性验证NettyClientNettyServerNettyClientTestHandlerNettyServerTestHandler结果上一章已经说了怎么解决沾包和拆包的问题&#xff0c;但是这样离一个成熟的通信…

【华为OD机试模拟题】用 C++ 实现 - 停车场最大距离(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 分积木(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 吃火锅(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - RSA 加密算法(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 构成的正方形数量(2023.Q1) 【华为OD机试模拟…

MyBatis快速开发

查询user表中的所有数据 步骤&#xff1a; 创建user表 打开Navicat&#xff0c;新建查询&#xff0c;将下面SQL代码复制粘贴并执行&#xff1a; create database mybatis; use mybatis;drop table if exists tb_user;create table tb_user(id int primary key auto_incremen…

抽象类与接口的区别

抽象类什么是抽象类&#xff1f;抽象类是特殊的类&#xff0c;只是不能被实例化&#xff1b;除此以外&#xff0c;具有类的其他特性&#xff1b;重要的是抽象类可以包括抽象方法&#xff0c;这是普通类所不能的。抽象方法只能声明于抽象类中&#xff0c;且不包含任何实现&#…

Docker安装YApi

目录0、Docker 环境准备1、数据库准备 MongoDB2、启动 YAPI3、官网教程0、Docker 环境准备 Docker 容器之间网络互通需要使用 docker network create yapi 创建一个自定义网络 docker network create yapi1、数据库准备 MongoDB YAPI 的数据库是 MongoDB&#xff0c;准备镜像…

Java线程池使用与原理解析1(线程池优点、使用方法、参数含义及线程池运转机制)

为什么要使用线程池&#xff1f; JDK1.5后JUC包添加了线程池相关接口&#xff0c;在Java诞生之初并没有线程池这个概念。刚开始Java程序都是自行创建线程去处理任务。随着应用使用的线程越来越多&#xff0c;JDK开发者们发现有必要使用一个统一的类来管理这些线程&#xff0c;…

从应届毕业生到大型开源社区 Committer,我做对了什么?

熟悉李成龙的人都叫他小龙。“小龙&#xff0c;2.2.3 Milvus 版本&#xff0c;日志会一直积累在磁盘上吗&#xff1f;默认会定期清除吗&#xff1f;”“小龙&#xff0c;请教一个问题&#xff0c;为啥取不到 field 中的 type 字段&#xff1f;”“小龙……”熟悉 Milvus 社区群…

django项目中如何添加自定义的django command

项目目录 1.我们自己建立的application叫做app&#xff0c;首先在这个app目录下&#xff0c;我们需要新建management目录&#xff0c;这个目录里应该包括&#xff1a;__ init__.py&#xff08;内容为空&#xff0c;用于打包&#xff09;和commands目录&#xff0c;然后在comma…

Linux中使用Docker部署Mysql数据库

前言 和朋友一起搞一个项目&#xff0c;分了一下工作&#xff0c;但是mysql迟迟安装不上&#xff0c;程序都在一个环境里确实容易出现很多问题&#xff0c;浪费时间和经历在这些配置上&#xff0c;好在有docker了&#xff0c;就在docker里搭建一个Mysql数据库使用吧&#xff0…

【计算机网络】计算机网络

目录一、概述计算机网络体系结构二、应用层DNS应用文件传输应用DHCP 应用电子邮件应用Web应用当访问一个网页的时候&#xff0c;都会发生什么三、传输层UDP 和 TCP 的特点UDP 首部格式TCP 首部格式TCP 的三次握手TCP 的四次挥手TCP 流量控制TCP 拥塞控制三、网络层IP 数据报格式…

李宏毅《机器学习》Bert笔记

李宏毅《机器学习》Bert笔记和工作原理解释1.参考2. self-supervised learning--bert3. bert的一些用法3.1情感分析3.2词性标注3.3常识推理和NLI3.4 QA问题4.bert的工作原理解释1.参考 bert论文 李宏毅《机器学习》自监督训练-bert 2. self-supervised learning–bert Bert模…