android四大组件之一-Activity实现原理分析

news2024/11/16 23:54:20

前言:

这篇文章是我花费时间最久的一篇文章,整整的两个月。整个流程繁琐是一个方面的原因,另外一个原因是我想尽可能的把整个流程的逻辑尽可能详细的一一描述出来,以及结合到我们项目中遇到的一些问题来进行解释,毕竟,我们学习Activity的启动原理的目的之一,就是为了方便我们更好的去排查解决问题,所以,这应该是网上可以搜到的关于Activity启动流程中最详细和完整的文章了。

本篇文章主要会讲以下内容:

1.Activity启动流程的基本介绍;

2.APP侧启动Activity的流程;

3.系统侧处理请求启动Activity的流程;

4.APP收到系统通知后生成Activity和执行其生命周期的完整流程;生命和唤起Activity的流程;

5.一些关于Activity的扩展性问题;

6.总结

PS:本文基于android13的源码进行讲解,推荐阅读时间1小时。

一.Activity启动流程的基本介绍

Activity启动基本流程如下:

1.APP首先通知系统侧去执行相关流程。

2.系统侧首先暂停栈顶的应用界面,如果进程不存在,还需要去尝试创建相关进程。

3.通知APP去完成Activity的创建以及执行其生命周期,

4.系统收到创建成功的回调后,通知上一个应用执行onStop或onDestory进行收尾。

所以按照以上的基本流程,同一个应用内,页面A跳转页面B的话并且调用finish的话,整个生命周期流程应该是如下这样的:

onPause(A)->onCreate(B)->onStart(B)->onResume(B)->onStop(A)->onDestory(A)

为什么流程是这样的?这样的设计又有什么合理性?以及系统又是在什么时候通知A或者B去执行对应生命周期的呢?接下来,我们就带着这些问题,一起去了解一下Activity启动的完整流程。

二.APP侧启动Activity的流程

2.1 APP侧启动简介:

2.1.1 启动主要流程

app侧启动activity,其实主要分为三个流程:

  1. Activity或Context完成相关的启动调用;

  2. 通过代理类Instrumentation中的方法完成调用;

  3. 通过ActivityTaskManager的binder应用,完成对系统侧的通知。

2.1.2 APP侧核心类介绍

介绍

Activity

这个不用过多介绍了,我们最熟悉的界面容器

ContextWrapper

Context的包装类,Activity/Service/Application等都集成ContextWrapper,而startActivity,startService,registerReceiver等方法,都来自这个这个类。

ContextImpl

Context的最终实现类,所有集成Context的类中使用到的相关方法,最终都是通过ContextWrapper中的ContextImpl这个实现类来实现的。

Instrumentation

功能代理类。和系统侧通信的部分,大多数都封装在了Instrumentation中完成的。

ActivityClient

APP侧的观察类,负责观察Activity生命周期的变化,通知系统侧。

ActivityClientController

系统侧的观察者接收类,负责接收ActivityClient传递过来的Activity生命周期的变化。

ActivityThread

应用的核心类,四大组件在APP侧的执行都由其负责。

ActivityThread.ApplicationThread

负责和系统侧交互,收到系统侧的调用后通知ActivityThread去完成最终的执行

2.2 Activity或Context完成相关的启动调用;

这里的启动Activity的流程,要区分调用对象是Activity还是Context类型的。

Activity继承自Context类,虽然Context中已经有了startActivity方法的实现,但是Activity中重写了startActivity方法。所以调用者的对象不一样,执行流程是不一样的。

我们先看一下Activity中的实现:

@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    ...
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        // Note we want to go through this call for compatibility with
        // applications that may have overridden the method.
        startActivityForResult(intent, -1);
    }
}

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {
    ...
    Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
}

首先,startActivity会调用到重载方法startActivity,最终都会调用到startActivityForResult方法。startActivityForResult这个方法中,会调用到Instrumentation中的execStartActivity方法,这里我们注意一下参数,等会会和ContextImpl中启动的参数相对比。

ContextImpl中的相关实现如下:

@Override
public void startActivity(Intent intent) {
    warnIfCallingFromSystemProcess();
    startActivity(intent, null);
}

@Override
public void startActivity(Intent intent, Bundle options) {
    warnIfCallingFromSystemProcess();
    ...
    mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity) null, intent, -1, options);
}

ContextImp中则直接是调用了execStartActivity方法。

同样都是Instrumentation中的execStartActivity方法,我们来看下参数的区别:

参数名

类型

介绍

Activity中的值

ContextImpl中的值

who

Context

用户获取上下文中的内容

Activity对象

ContextImpl对象

contextThread

IBinder

ApplicationThread的binder引用

mMainThread.getApplicationThread()

mMainThread.getApplicationThread()

token

IBinder

Activity的binder引用,标识唯一Activity

mToken

null

target

Activity

启动的Activity

Activity对象

null

intent

Intent

启动的意图对象

intent

intent

requestCode

int

回调code

requestCode

-1

options

Bundle

启动配置参数

options

options

所以,无论哪种形式启动,最终都是调用到Instrumentation中的execStartActivity方法。Activity和Context中的startActivity方法,穿惨两者的区别,其实就是Activity的启动时,token/target/requestCode等参数有值,而这些参数都是为forResult准备的。虽然Activity中重写了startActivity方法,但是其流程上和Context仍然是一样的,调用了Instrumentation中的execStartActivity方法。而Activity中的startActivityForResult方法,虽然也是调用了Instrumentation中的execStartActivity方法,参但是传参不一样。

2.3 Instrumentation中的相关流程

Instrumentation中的启动流程还是比较简单的,只有两个功能:

1.监控Activity启动;

2.调用AMS中方法。

我们来挨个介绍下。

2.2.1 监控Activity启动

如果我们调用Instrumentation中的addMonitor方法添加ActivityMonitor对象,则会添加观察者对象。这个观察者主要就是观察Activity的启动,甚至于这个观察者还可以通过返回值,来决定是否阻断Activity的启动流程。

    if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    ActivityResult result = null;
                    if (am.ignoreMatchingSpecificIntents()) {
                        if (options == null) {
                            options = ActivityOptions.makeBasic().toBundle();
                        }
                        result = am.onStartActivity(who, intent, options);
                    }
                    if (result != null) {
                        am.mHits++;
                        //直接返回,不启动
                        return result;
                    } else if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }

2.2.2 调用AMS中方法

首先,ActivityTaskManager.getService()方法中,通过ServiceManager获取到系统侧的ActivityTaskManagerService的引用binder。

然后,调用binder方法startActivity通知系统去完成Activity的启动流程。

相关代码人如下:

int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getOpPackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
                    requestCode, 0, null, options);
checkStartActivityResult(result, intent);

public static void checkStartActivityResult(int res, Object intent) {
    if (!ActivityManager.isStartResultFatalError(res)) {
        return;
    }
    switch (res) {
        case ActivityManager.START_INTENT_NOT_RESOLVED:
            throw new ActivityNotFoundException()
        ...
}

值得注意的是,如果调用系统方法的最终返回值result不是sucess,则会根据错误码result来抛出对应的异常,所以,我们可以根据不同的异常类型,来判断到底是何原因导致启动失败。

错误码

原因

ActivityManager.START_SUCCESS

成功

ActivityManager.START_INTENT_NOT_RESOLVED

未在manifest中声明配置,或者有声明但是类不存在,3.3.4中有讲到。

ActivityManager.START_CLASS_NOT_FOUND

Activity类找不到,,3.3.4中有讲到。

ActivityManager.START_PERMISSION_DENIED

启动进程非法,3.3.2中有讲

ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT

通过forResult方式启动,配置启动参数冲突,3.3.3中有讲

ActivityManager.START_NOT_ACTIVITY

ActivityManager.START_NOT_VOICE_COMPATIBLE

不支持语音唤醒,3.3.4中有讲到。

ActivityManager.START_VOICE_NOT_ACTIVE_SESSION

ActivityManager.START_VOICE_HIDDEN_SESSION

ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION

ActivityManager.START_ASSISTANT_HIDDEN_SESSION

ActivityManager.START_CANCELED

其它

比如ActivityManager.START_ABORTED等等

最后我们整理一下传递给系统侧的参数,后面系统侧会被用到。

参数名

类型

介绍

Instrumentation中的值

whoThread

IApplicationThread

APP侧提供的binder引用

Activity对象

callingPackage

String

启动者的包名

APP的包名

callingFeatureId

String

区分同一个包名下不同模块的标识

null

intent

Intent

启动意图

intent

resolvedType

String

null

resultTo

IBinder

启动者的binder引用,用于区分不同的Activity

Activity中的mToken

resultWho

String

启动者的标记

null

requestCode

requestCode

启动时配置的requestCode,用于forResult的场景

根据启动时的设置来决定

startFlags

startFlags

启动标记

固定为0

ProfilerInfo

profilerInfo

系统应用启动时才会配置,用于传递配置

null

Bundle

bOptions

启动时的配置参数

options

三.系统侧处理启动的请求

系统侧的核心类介绍:

类型

介绍

ActivityManagerService

名字虽然是Activity开头,但是实际上确实负责应用的启动和四大组件。

ActivityTaskManagerService

因为AMS中负责的事情太多,专门把Activity的流程从AMS中抽出,负责Activity启动相关的流程。

ActivityStarter

具体负责执行Activity启动流程的类,负责了启动流程中的大多数逻辑。

Task

其实一个对象类,Task中装载多个Activity对象;

TaskFragment

Task的父类,可以状态Activity或者其他的TaskFragment。管理Activity生命周期并更新其中活动的可见性。

ActivityTaskSupervisor

原来负责Task逻辑的核心类,但是目前的安卓高版本其中的功能已被其它类替代掉。

目前承担的功能是查找解析manifest中的注册。

系统侧收到请求后,主要会完成以下几件事:

方法

功能

ActivityTaskManagerService.

startActivityAsUser

生成ActivityStarter类,这个类负责完成整个Activity的启动流程。

启动参数都添加到Request对象上,整个流程中用到的各种配置参数都从Request中取。

ActivityStarter.execute

做一些准备的工作,继续给mRequest对象赋值,然后开启下一个流程。等流程结束后,还负责回收ActivityStarter对象。

ActivityStarter.executeRequest

进行各种检查,然后构建ActivityRecord对象,开启下一个流程。

ActivityStarter.startActivityUnchecked

生成事务,开启下一个流程。

ActivityStarter.startActivityInner

前面都是准备工作,这里是启动的核心逻辑。首先根据Activity的启动类型,加入到合适的task中,然后进入下一个流程。

Task.resumeTopActivityUncheckedLocked

把目标Task挪到前台显示。

上面的这些主流程,我们来一一详细解释。

3.1 生成ActivityStarter开启流程

3.1.1 生成ActivityStarter对象

ActivityTaskManagerService中的startActivity方法收到请求后,会直接调用startActivityAsUser方法完成开启流程,所以下面的代码也都是startActivityAsUser方法中的。

首先,根据intent和reason通过obtainStarter方法构造生成ActivityStarter对象,这里构造ActivityStarter使用了设计模式中的享元模式和建造者模式。必要的参数是intent和reason,这两个值会添加到ActivityStarter中的Request对象上。

getActivityStartController().obtainStarter(intent, "startActivityAsUser")

3.1.2 添加参数

然后,给Request添加一些其它的参数。

.setCaller(caller)
.setCallingPackage(callingPackage)
.setCallingFeatureId(callingFeatureId)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setUserId(userId)

所以,我们理一下,Request中共有以下参数是赋值了。

参数类型

变量名

介绍

Intent

intent

携带启动参数和启动目标

String

reason

启动流程中固定为"startActivityAsUser"

IApplicationThread

caller

APP进程中ActivityThread类中ApplicationThread的引用。APP传递过来

String

callingPackage

APP的包名

String

callingFeatureId

Context的属性标记,默认值为null

String

resolvedType

APP一般不用,系统用的参数

IBinder

resultTo

调用启动的Activity持有,使用forResult的方式启动时,完成时调用进行通知的binder。

String

resultWho

识别返回人,一般为null

int

requestCode

请求code,用于识别返回类型

int

startFlags

启动流程中固定为0

String

profilerInfo

启动流程中固定为null

Bundle

bOptions

启动Activity的其它选项

int

userId

用户ID,要区分与PID的区别

最后,通过execute();方法开启整个Activity的启动流程。

总结一下,这个环节就是首先利用ActivityStartController生成ActivityStart对象,然后添加必要的启动参数,这些参数最终都会被记录到ActivityStart.Request这个对象当中,最后通过execute方法开启流程。

3.2 execute流程

该流程主要是前期的准备工作,比如成员变量resolveInfo/activityInfo赋值等,以及一些合法性校验

主要包含完成了以下4步:

流程1:给Request中的resolveInfo和activityInfo赋值;

流程2:执行重载进程逻辑;

流程3:执行下一个启动流程;

流程4:回收ActivityStarter对象。

相关调用出代码如下:

public class ActivityStarter{
    int execute() {
        try{
            //1.给Request中的resolveInfo和activityInfo赋值;
            if (mRequest.activityInfo == null) {
                mRequest.resolveActivity(mSupervisor);
            }
            ...
            //2.执行重载进程逻辑;
            res = resolveToHeavyWeightSwitcherIfNeeded();
            if (res != START_SUCCESS) {
                return res;
            }
            ...
            //3.执行下一个启动流程;
            res = executeRequest(mRequest);
            ...
        } finally {
            //4.回收ActivityStarter对象;
            onExecutionComplete();
        }
    }
}

3.2.1 resolveInfo和activityInfo赋值

Request对象中,存在resolveInfo和activityInfo两个对象,resolveInfo对应在manifest中的节点配置信息,activityInfo对应Activity的配置信息。很明显,activityInfo的范围比resolveInfo更小,因为service/broadcast的配置也都属于resolveInfo中的信息。

对这两个对象赋值,主要是通过Request中的resolveActivity方法来实现的。

//ActivityStarter
int execute() {
    if (mRequest.activityInfo == null) {
        mRequest.resolveActivity(mSupervisor);
    }
}
//ActivityStarter.Request
void resolveActivity(ActivityTaskSupervisor supervisor) {
    realCallingPid = Binder.getCallingPid();
    realCallingUid = Binder.getCallingUid();
    ...
    resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,0 /* matchFlags */,computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid));
    if (resolveInfo == null) {
        if (profileLockedAndParentUnlockingOrUnlocked) {
            resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,computeResolveFilterUid(callingUid, realCallingUid,filterCallingUid));
        }
    }
    activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags, profilerInfo);
    if (activityInfo != null) {
        intentGrants = supervisor.mService.mUgmInternal.checkGrantUriPermissionFromIntent(intent, resolvedCallingUid, activityInfo.applicationInfo.packageName,UserHandle.getUserId(activityInfo.applicationInfo.uid));
    }
}

resolveInfo和activityInfo分别通过ActivityTaskSupervisor的resolveIntent和resolveActivity方法进行查询和赋值。

所以,我们先来看下resolveInfo的获取流程:

//ActivityTaskSupervisor.java
public class ActivityTaskSupervisor{
    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags, int filterCallingUid) {
        ...
        return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,filterCallingUid);
    }
}

//android13中是PackageManagerInternalBase,其它版本是PackageManagerService.PackageManagerInternalImpl
//PackageManagerInternalBase.hava
public class PackageManagerInternalBase{
    public final ResolveInfo resolveIntent(Intent intent, String resolvedType, ...){
        return getResolveIntentHelper().resolveIntentInternal(snapshot(),intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,filterCallingUid);
    }
}

//ResolveIntentHelper.java
public class ResolveIntentHelper {
    public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,...)
        ...
        final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,resolvedType, flags, privateResolveFlags, filterCallingUid, userId,resolveForStart, true /*allowDynamicSplits*/);
        ...
        final ResolveInfo bestChoice = chooseBestActivity(computer, intent, resolvedType, flags, privateResolveFlags, query, userId, queryMayBeFiltered);
        return bestChoice;
    }
}

//ComputerEngine.java
public class ComputerEngine {
    public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,String resolvedType,...){
        final String pkgName = intent.getPackage();
        ComponentName comp = intent.getComponent();
        List<ResolveInfo> list = Collections.emptyList();
        if (comp != null) {
            //显式
            final ActivityInfo ai = getActivityInfo(comp, flags, userId);
            final ResolveInfo ri = new ResolveInfo();
            ri.activityInfo = ai;
            list = new ArrayList<>(1);
            list.add(ri);
        } else {
            //隐式
            QueryIntentActivitiesResult lockedResult = queryIntentActivitiesInternalBody();
            list = lockedResult.result;
        }
        return skipPostResolution ? list:...;
    }
    
    protected ActivityInfo getActivityInfoInternalBody(ComponentName component,@PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
        ParsedActivity a = mComponentResolver.getActivity(component);
        AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
        if (pkg != null && mSettings.isEnabledAndMatch(pkg, a, flags, userId)) {
            ...
            return PackageInfoUtils.generateActivityInfo(pkg, a, flags, ps.getUserStateOrDefault(userId), userId, ps);
        }
        if (resolveComponentName().equals(component)) {
            return PackageInfoWithoutStateUtils.generateDelegateActivityInfo(mResolveActivity, flags, PackageUserStateInternal.DEFAULT, userId);
        }
        return null;
    }
    
    public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(...){
        ...
    }
}

我们可以看到,通过层层传递,最终通过ResolveIntentHelper中的resolveIntentInternal方法来实现的。

首先,查询出所有符合intent中条件的,返回一个List集合。queryIntentActivitiesInternal方法中我们可以得知,优先显示查找,显示不存在时,才会选择隐式的查找方式。

最后从符合条件的List中选择最合适的那个进行返回。这里具体的查找的业务逻辑就不过多描述了,有兴趣的读者可以自行查询。

接下来我们来看我们先来看resolveActivity的获取流程:

//ActivityTaskSupervisor.java
public class ActivityTaskSupervisor{
    //这里的rInfo是上面查找出来的
    ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags, ProfilerInfo profilerInfo) {
        final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
        if (aInfo != null) {
            //这里添加ComponentName,后面会用到
            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
            ...
            final String intentLaunchToken = intent.getLaunchToken();
            if (aInfo.launchToken == null && intentLaunchToken != null) {
                aInfo.launchToken = intentLaunchToken;
            }
        }
        return aInfo;
    }
}

这里我们看到,虽然名字叫resolveActivity,但是实际上并没有执行什么解析的操作,而是直接使用上一流程中查找出来的ResolveInfo对象中的ActivityInfo,在校准launchToken属性后,就直接返回了。

至此,Request中的resolveInfo和activityInfo都已经有值了。

3.2.2 重载进行判断

通过resolveToHeavyWeightSwitcherIfNeeded方法进行重载判断。

这个流程中,根据注释中的翻译,如果当前进程是一个重载进程,而另一个不同的重载进程正在运行,则切换到另外一个进程进行启动请求。

这个逻辑不是主流程的,所以这里我们就不深入讨论了。

3.2.3 executeRequest流程

之前的传入以及查找到的值都会被添加到了ActivityStarter.Request对象上,调用executeRequest方法传入mRequest,开启下一个流程。

具体内容我们下一小节详细讲解。

3.2.4 onExecutionComplete回收

这里就是典型的享元模式了,因为上面executeRequest方法执行完,代表整个流程执行完成,则使用onExecutionComplete方法对ActivityStarter对象进行回收。

//ActivityStarter.java
class ActivityStarter{
    private void onExecutionComplete() {
        mController.onExecutionComplete(this);
    }
}

//ActivityStartController.java
public class ActivityStartController{
    void onExecutionComplete(ActivityStarter starter) {
        if (mLastStarter == null) {
            mLastStarter = mFactory.obtain();
        }
        mLastStarter.set(starter);
        mFactory.recycle(starter);
    }
}

3.3 executeRequest环节

该流程主要是对准备数据的二次加工使之变成后面可用数据,并且做一些权限的检查,以及生成ActivityRecord对象。

主要分成以下几项:

1.入参准备

2.打印日志,代表准备工作完成,正式开始启动流程;

3.确定forResult返回对象;

4.入参校验;

5.启动权限检查

6.后台启动权限检查

7.构建ActivityRecord对象

8.进入下一阶段流程

class ActivityStarter{
    private int executeRequest(Request request) {
        //1.把Request中的成员变量,复制给方法内的变量,供后面使用;
        mLastStartReason = request.reason;
        ...
        final IApplicationThread caller = request.caller;
        ...
        //2.打印日志,代表准备工作完成,正式开始启动流程;
        if (err == ActivityManager.START_SUCCESS) {
            Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)+ "} from uid " + callingUid);
        }
        //3.
        ActivityRecord sourceRecord = null;
        ActivityRecord resultRecord = null;
        if (resultTo != null) {
            sourceRecord = ActivityRecord.isInAnyTask(resultTo);
            ...
        }
        
        //4.入参校验
        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
            // We couldn't find a class that can handle the given Intent.
            // That's the end of that!
            err = ActivityManager.START_INTENT_NOT_RESOLVED;
        }
        ...
        //5.启动权限检查
        boolean abort = !mSupervisor.checkStartAnyActivityPermission(...)
       
        //6.后台启动权限检查
        restrictedBgActivity = shouldAbortBackgroundActivityStart();
        
        //7.构建ActivityRecord对象
        final ActivityRecord r = new ActivityRecord.Builder(mService)....build();
        mLastStartActivityRecord = r;
        
        //8.进入下一阶段流程
        mLastStartActivityResult = startActivityUnchecked(...)
        return mLastStartActivityResult;
    }
}

3.3.1 入参准备

private int executeRequest(Request request) {
    mLastStartReason = request.reason;
    mLastStartActivityTimeMs = System.currentTimeMillis();
    mLastStartActivityRecord = null;
    
    final IApplicationThread caller = request.caller;
    Intent intent = request.intent;
    NeededUriGrants intentGrants = request.intentGrants;
    String resolvedType = request.resolvedType;
    ActivityInfo aInfo = request.activityInfo;
    ResolveInfo rInfo = request.resolveInfo;
    final IVoiceInteractionSession voiceSession = request.voiceSession;
    final IBinder resultTo = request.resultTo;
    String resultWho = request.resultWho;
    int requestCode = request.requestCode;
    int callingPid = request.callingPid;
    int callingUid = request.callingUid;
    String callingPackage = request.callingPackage;
    String callingFeatureId = request.callingFeatureId;
    final int realCallingPid = request.realCallingPid;
    final int realCallingUid = request.realCallingUid;
    final int startFlags = request.startFlags;
    final SafeActivityOptions options = request.activityOptions;
    Task inTask = request.inTask;
    TaskFragment inTaskFragment = request.inTaskFragment;
}

把Request中的成员变量,赋值给方法内的临时变量,供后面使用,这里,我们重点记录以下几个,后面会用到。

类型

变量名

来源

IApplicationThread

caller

request.caller

Intent

intent

request.intent

IBinder

resultTo

equest.resultTo

3.3.2 打印启动日志

第二块逻辑代码如下:

int err = ActivityManager.START_SUCCESS;
WindowProcessController callerApp = null;
//进程判断
if (caller != null) {
    callerApp = mService.getProcessController(caller);
    if (callerApp != null) {
        callingPid = callerApp.getPid();
        callingUid = callerApp.mInfo.uid;
    } else {
        Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid
                + ") when starting: " + intent.toString());
        err = START_PERMISSION_DENIED;
    }
}
//打印启动日志
final int userId = aInfo != null && aInfo.applicationInfo != null
        ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
if (err == ActivityManager.START_SUCCESS) {
    Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
            + "} from uid " + callingUid);
}

主要做了两块逻辑:

1.首先进行一个合法性校验,判断caller是否是正常的进程,如果是,才会继续执行。否则返回错误码:START_PERMISSION_DENIED

2.打印一条启动日志,这个日志,是一定会有的,并不需要debug环境。如果看到log中有了这条日志,就证明已经走到了系统侧的流程了。

下面的日志就是一个样例:

START u0 {cmp=com.xt.client/.activitys.test.Test1Activity} from uid 10235

u0:目标Activity归属进程,如果进程还未创建,就是0;

cmp=com.xt.client/.activitys.test.Test1Activity:启动的目标Activity;

Uid 10235:调用者进程。

3.3.3 确定forResult返回对象

接下来就是给sourceRecord和resultRecord对象赋值,这两个对象,主要是通过startActivityForResult()启动时用来执行回调的。

sourceRecord代表从哪个Activity发起的启动请求,

resultRecord代表从有返回结果时通知到哪个Activity。

这两者仅在resultTo不为空时才会有值,也就是说只有调用startActivityForResult启动时,才会值。

 class ActivityStarter{
     private int executeRequest(Request request) {
         ...
         ActivityRecord sourceRecord = null;
         ActivityRecord resultRecord = null;
         if (resultTo != null) {
                sourceRecord = ActivityRecord.isInAnyTask(resultTo);  
                if (sourceRecord != null) {
                    if (requestCode >= 0 && !sourceRecord.finishing) {
                        resultRecord = sourceRecord;
                    }
                }
          } 
          final int launchFlags = intent.getFlags();
          if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
              if (requestCode >= 0) {
                  return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
              }
          }
     }
 }

1.当requestCode>0并且sourceRecord没有被finish,就会把sourceRecord赋值给resultRecord。

2.如果是通过forResult的方式,并且requestCode>=0时,这种情况是要有返回值的。这种情况,是不能使用FLAG_ACTIVITY_FORWARD_RESULT参数的,否则返回错误START_FORWARD_AND_REQUEST_CONFLICT

3.3.4 参数校验

上一流程3.2.1中,如果找到了对应的配置,则会给对应的参数赋值。而如果没有找到,则这些参数就是空的,所以这里进行参数校验检查,看已经有值的这些参数是否满足启动的需求。

首先,检查3.2.1中赋值的intent中的ComponentName是否为空,如果为空,则代表manifest未配置。

if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
    // We couldn't find a class that can handle the given Intent.
    // That's the end of that!
    err = ActivityManager.START_INTENT_NOT_RESOLVED;
}

然后,检查Request中的activityInfo是否为空,如果为空,返回错误码START_CLASS_NOT_FOUND,代表类不存在。

if (err == ActivityManager.START_SUCCESS && aInfo == null) {
    // We couldn't find the specific class specified in the Intent.
    // Also the end of the line.
    err = ActivityManager.START_CLASS_NOT_FOUND;
}

继续做检查,如果是语音会话启动的,确保安全。如果失败,返回错误码START_NOT_VOICE_COMPATIBLE,代表不支持语音唤醒。

if (err == ActivityManager.START_SUCCESS && sourceRecord != null
        && sourceRecord.getTask().voiceSession != null) {
    // If this activity is being launched as part of a voice session, we need to ensure
    // that it is safe to do so.  If the upcoming activity will also be part of the voice
    // session, we can only launch it if it has explicitly said it supports the VOICE
    // category, or it is a part of the calling app.
    if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0
            && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
        try {
            intent.addCategory(Intent.CATEGORY_VOICE);
            if (!mService.getPackageManager().activitySupportsIntent(
                    intent.getComponent(), intent, resolvedType)) {
                Slog.w(TAG, "Activity being started in current voice task does not support "
                        + "voice: " + intent);
                err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Failure checking voice capabilities", e);
            err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
        }
    }
}
if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
    // If the caller is starting a new voice session, just make sure the target
    // is actually allowing it to run this way.
    try {
        if (!mService.getPackageManager().activitySupportsIntent(intent.getComponent(),
                intent, resolvedType)) {
            Slog.w(TAG,
                    "Activity being started in new voice task does not support: " + intent);
            err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
        }
    } catch (RemoteException e) {
        Slog.w(TAG, "Failure checking voice capabilities", e);
        err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
    }
}

最后,判断一下,如果err!=START_SUCCESS,则证明上面的检查不过,则返回启动失败。

if (err != START_SUCCESS) {
    if (resultRecord != null) {
        resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
                null /* data */, null /* dataGrants */);
    }
    SafeActivityOptions.abort(options);
    return err;
}

3.3.5 启动权限检查

相关代码如下:

 class ActivityStarter{
     private int executeRequest(Request request) {
         ...
         boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
                requestCode, callingPid, callingUid, callingPackage, callingFeatureId,
                request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord,
                resultRootTask);
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                callingPid, resolvedType, aInfo.applicationInfo);
         abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
                callingPackage);
         ...
         if (abort) {       
             return START_ABORTED;
         } 
      }
}

首先设置标记位abort,来记录是放弃本次启动,包括下一小节中的后台启动检查也是一样,阻断性的判断,只要有一次true,就会返回START_ABORTED的错误码。

总共有三块判断:

首先,判断应用是否具有启动Activity的权限。是的,启动Activity也需要权限,只是正常应用默认都具有;

然后,判断是否通过防火墙验证;

最后,检查某些敏感的Action类型(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER和Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT),启动者的targetSDK是否为高版本,如果是,则不允许此次操作。

3.3.6 后台启动检查

安卓13中,加入了后台应用启动的判断。如果某个后台的应用直接使用service启动Activity,默认是启动不起来的。

如果没有放弃本次启动的话,则会通过shouldAbortBackgroundActivityStart方法检查是否允许后台启动。也

就是说restrictedBgActivity=true时,最终会放弃本地启动,反之则允许。

boolean restrictedBgActivity = false;
if (!abort) {
    restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
            callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
            request.originatingPendingIntent, request.allowBackgroundActivityStart,
            intent, checkedOptions);
}

至于最终如何放弃本次启动的,我们后面会讲。这里我们主要讲一下shouldAbortBackgroundActivityStart方法中是如何进行判断的。

boolean shouldAbortBackgroundActivityStart(...){
    final int callingAppId = UserHandle.getAppId(callingUid);
    if (useCallingUidState) {
        //系统APP,允许后台启动
        if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID || callingAppId == Process.NFC_UID) {
            return false;
        }
        //桌面APP,则也允许后台启动
        if (isHomeApp(callingUid, callingPackage)) {
            return false;
        }
        //输入法应用
        final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();
        if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
            return false;
        }
    }
    //应用是否处于系统豁免名单中
    if (useCallingUidState && allowCallingUidStartActivity) {
        return false;
    }
    ...略过一些不重要的判断
    if (useCallingUidState) {
        //如果应用具有START_ACTIVITIES_FROM_BACKGROUND权限,则允许后台启动
        if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) {
            return false;
        }
        //如果最近有活动,则允许。
        if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
            return false;
        }
        //设备所有者,则允许
        if (mService.isDeviceOwner(callingUid)) {
            return false;
        }
        //如果应用具有系统弹窗权限,则允许
        if (mService.hasSystemAlertWindowPermission(callingUid,callingPid, callingPackage)) {
            return false;
        }
    }
    //如果应用配置允许后台启动,则允许
    if (callerApp != null && useCallingUidState) {
        if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
            return false;
        }
        final ArraySet<WindowProcessController> uidProcesses = mService.mProcessMap.getProcesses(callerAppUid);
        if (uidProcesses != null) {
            for (int i = uidProcesses.size() - 1; i >= 0; i--) {
                final WindowProcessController proc = uidProcesses.valueAt(i);
                if (proc != callerApp&& proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
                    ...
                    return false;
                }
            }
        }
    }
    
    Slog.w(TAG, "Background activity start [callingPackage: " );
    return true;
}

所以我们总结一下,有如下场景,当然,本文中省略了很多场景,实际要比这个更多。

第一类:系统应用/桌面应用/输入法应用;

第二类:应用处于系统豁免白名单中;

第三类:最近有活动,或者设备拥有者。实际上,最近活动这个很难定义,往往可以认为是否。

第四类:具有START_ACTIVITIES_FROM_BACKGROUND/系统弹窗权限。这一类也是我们最常用的,通过应用申请相应的权限,来实现后台弹出Activity的效果。

第五类:看后台进程控制器中的判断,具体实现方法是:BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed()方法。这里就不扩展了。

如果满足以上条件,则restrictedBgActivity=false,否则则为true。最终restrictedBgActivity传递到后面流程,来影响Activity启动流程。

3.3.7 构建ActivityRecord对象

前面所有的检查都做完了,那么接下来就可以准备开始启动了,这里先做一个准备工作,通过建造者模式构建ActivityRecord对象,ActivityRecord对象和APP一侧的Activity一一对应,是Activity的在系统侧的代表。

最后,把新构建的ActivityRecord对象赋值给mLastStartActivityRecord,代表最近一个正在启动的Activity。

final ActivityRecord r = new ActivityRecord.Builder(mService)
        .setCaller(callerApp)
        .setLaunchedFromPid(callingPid)
        .setLaunchedFromUid(callingUid)
        .setLaunchedFromPackage(callingPackage)
        .setLaunchedFromFeature(callingFeatureId)
        .setIntent(intent)
        .setResolvedType(resolvedType)
        .setActivityInfo(aInfo)
        .setConfiguration(mService.getGlobalConfiguration())
        .setResultTo(resultRecord)
        .setResultWho(resultWho)
        .setRequestCode(requestCode)
        .setComponentSpecified(request.componentSpecified)
        .setRootVoiceInteraction(voiceSession != null)
        .setActivityOptions(checkedOptions)
        .setSourceRecord(sourceRecord)
        .build();

mLastStartActivityRecord = r;

3.3.8 进入下一个流程

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,...) {
    //1.延迟布局
    mService.deferWindowLayout();
    //2.进入下一环节
    result = startActivityInner()
    //3.
    tartedActivityRootTask = handleStartResult()
    //4.
    postStartActivityProcessing()
}

调用startActivityUnchecked方法,进入下一阶段的启动流程。

startActivityUnchecked返回的错误码,则直接抛给上层,返回给应用层对应的错误码。

3.3 startActivityUnchecked环节

这个环节中,逻辑比较简单,主要有以下几块逻辑,代码如下:

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,...) {
    //1.延迟布局
    mService.deferWindowLayout();
    //2.进入下一环节
    result = startActivityInner()
    //3.
    tartedActivityRootTask = handleStartResult()
    //4.
    postStartActivityProcessing()
}

1.调用mService.deferWindowLayout方法,延迟当前进行的布局。因为页面即将要刷新,继续进行布局没有意义。

2.调用startActivityInner方法,进入下一环节;

3.通过handleStartResult方法,二次检查被拉起的targetActivity处于前台显示;

4.通过postStartActivityProcessing方法,向外发送通知,告之启动完成。

3.4 startActivityInner环节

startActivityInner中,主要负责的工作是给Activity安排一个适合的栈,然后使这个栈显示到前台。

介绍栈,我们先普及一下必备的知识,栈分为子栈和根栈。子栈的话,是必须归属同一个应用的,但是根栈是可以跨应用的。那么什么是子栈还是根栈呢?

在某个页面不断点击返回按钮返回上一界面,只要能够返回到达的页面,都属于同一个根栈。而子栈只是一个应用中,对若干个Activity记录的分组封装。

简单来说,A应用的a页面跳转B应用的b页面,那么a和b页面归属两个子栈,但是同属一个根栈,所以根栈其实是和Affiliate相对应的。

然后我们回到正题,继续看启动流程的代码,如下:

class ActivityStarter {    
    ...
    int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,...) {
        //1.初始化ActivityStart的成员变量
        setInitialState()
        //2.校正mLaunchFlags值
        computeLaunchingTaskFlags();
        //3.校正sourceRecord
        computeSourceRootTask();
        //4.计算dreamStopping
        boolean dreamStopping = false;
        for (ActivityRecord stoppingActivity : mSupervisor.mStoppingActivities) {
            if (stoppingActivity.getActivityType()
                    == WindowConfiguration.ACTIVITY_TYPE_DREAM) {
                dreamStopping = true;
                break;
            }
        }
        //5.四个Task
        final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
        final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
        final Task reusedTask = getReusableTask();
        ...
        computeLaunchParams(r, sourceRecord, targetTask);
        //6.task权限检查
        int startResult = isAllowedToStart(r, newTask, targetTask);
        //7.新建或复用Task
        final ActivityRecord targetTaskTop = newTask ? null : targetTask.getTopNonFinishingActivity();
        if (targetTaskTop != null) {
            startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants);
            if (startResult != START_SUCCESS) {
                return startResult;
            }
        } else {
            mAddingToTask = true;
        }
        ...
        if (mTargetRootTask == null) {
            mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask, mOptions);
        }
        if (newTask) {
            final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                    ? mSourceRecord.getTask() : null;
            setNewTask(taskToAffiliate);
        } else if (mAddingToTask) {
            addOrReparentStartingActivity(targetTask, "adding to task");
        }        

        //8.修改栈中Activity顺序
        mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch, mOptions, sourceRecord);
        if (mDoResume) {
            //9.进入下一流程
            ...
        }
        //10.收尾工作
        mSupervisor.mRecentTasks.add(startedTask);
        return START_SUCCESS;
    }
}

主要包含以下几个环节:

1.初始化ActivityStart中的成员变量

2.校正mLaunchFlags值

3.校正sourceRecord

4.计算dreamStopping

5.四个Task

6.task权限检查

7.新建或复用Task

8.修改栈中Activity顺序

9.进入下一流程

10.收尾工作

3.4.1 初始化ActivityStart的成员变量

class ActivityStart{
    int startActivityInner(){
        setInitialState(r, options, inTask, inTaskFragment, doResume, startFlags, sourceRecord,voiceSession, voiceInteractor, restrictedBgActivity);
        ...
    }     
       
    private void setInitialState(...){
        mStartActivity = r;
        mIntent = r.intent;
        mOptions = options;
        mCallingUid = r.launchedFromUid;
        mSourceRecord = sourceRecord;
        mVoiceSession = voiceSession;
        mVoiceInteractor = voiceInteractor;
        mRestrictedBgActivity = restrictedBgActivity;
        ...
        mLaunchMode = r.launchMode;
        ...
        mDoResume = doResume;
    }    
}      
                
                

这里主要是参数的赋值,逻辑代码并不多,所以我们梳理其中参数的含义:

成员变量名

解释

mStartActivity

赋值给成员变量mStartActivity,目标activity的封装对象ActivityRecord

mIntent

启动意图对象,这里的intent是系统侧已经填充的,而不是原始的那个

mOptions

启动Activity的其它选项

mCallingUid

调用者的uid

mSourceRecord

来源Activity的系统侧对象ActivityRecord

mVoiceSession

语音相关的配置

mVoiceInteractor

语音相关的拦截器

mRestrictedBgActivity

是否限制后台启动

mPreferredTaskDisplayArea

启动Activity的任务在屏幕上的显示区域

mPreferredWindowingMode

上面那块显示区域的显示方式,全屏/浮窗/分屏

mLaunchMode

Activity的启动模式,对应五种启动模式,其定义在ActivityInfo中。

mLaunchFlags

启动的配置参数

mDoResume

正常启动流程时为true,除非额外标记为不想展示到前台

mNotTop

设置FLAG_ACTIVITY_PREVIOUS_IS_TOP标记时为启动targetActivity的那个Activity的ActivityRecord

mInTask

targetActivity期望加入的Task,正常启动时为null。

mInTaskFragment

targetActivity期望加入的TaskFragment,正常启动时为null。

mStartFlags

默认为0

mNoAnimation

是否不显示动画

mLaunchTaskBehind

是否需要复用Task的Affiliate,也就是说是否不需要新建根task。

所以singeTask和singleInstance是false

3.4.2 校正mLaunchFlags值

这里主要通过computeLaunchingTaskFlags方法来实现的,重新判断条件,看是否需要添加FLAG_ACTIVITY_NEW_TASK属性。这个属性值提供给后面使用,用来判断是否需要复用task。

主要是再次计算mLaunchFlags的值,如果来源Activity的启动方式是LAUNCH_SINGLE_INSTANCE,或者targetActivity的启动方式是LAUNCH_SINGLE_INSTANCE/LAUNCH_SINGLE_TASK,则给mLaunchFlags添加FLAG_ACTIVITY_NEW_TASK标记。

int startActivityInner(...){
    ...
    computeLaunchingTaskFlags();
    ...
}
private void computeLaunchingTaskFlags() {
    ...
    if (mInTask == null) {
        if (mSourceRecord == null) {
            // This activity is not being started from another...  in this
            // case we -always- start a new 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) {
            // The original activity who is starting us is running as a single
            // instance...  this new activity it is starting must go on its
            // own task.
            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
        } else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
            // The activity being started is a single instance...  it always
            // gets launched into its own task.
            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
        }
    }    
}

我们可以看到,如果是调用者Activity是LAUNCH_SINGLE_INSTANCE类型,或者目标Activity是LAUNCH_SINGLE_INSTANCE/LAUNCH_SINGLE_TASK类型,都会添加到FLAG_ACTIVITY_NEW_TASK属性。

这也符合我们的正常认知,调用者为singleInstance类型,自然不应该和调用者同一个栈。

而目标Activity为singleInstanc或singeTask类型,自然也不会和调用者同一个栈。疑问:singeTask为什么也不会?

至于mInTask不为空的场景,因为不是提供给正常启动流程使用的,所以这里就不扩展分析了。

3.4.3 校正sourceRecord

如果sourceRecord已经被finish了,则需要清空sourceRecord和mSourceRootTask的值。

private void computeSourceRootTask() {
    if (mSourceRecord == null) {
        mSourceRootTask = null;
        return;
    }
    if (!mSourceRecord.finishing) {
        mSourceRootTask = mSourceRecord.getRootTask();
        return;
    }

    if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
        Slog.w(TAG, "startActivity called from finishing " + mSourceRecord
                + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
        mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
        final Task sourceTask = mSourceRecord.getTask();
        if (sourceTask == null || sourceTask.getTopNonFinishingActivity() == null) {
            mNewTaskInfo = mSourceRecord.info;
            mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
        }
    }
    mSourceRecord = null;
    mSourceRootTask = null;
}

3.4.4 计算dreamStopping

首先,把校正后的mLaunchFlags赋值给intent,

然后,计算dreamStopping。dreamStopping结果在后面3.4.6中使用。

mIntent.setFlags(mLaunchFlags);

boolean dreamStopping = false;

for (ActivityRecord stoppingActivity : mSupervisor.mStoppingActivities) {
    if (stoppingActivity.getActivityType()
            == WindowConfiguration.ACTIVITY_TYPE_DREAM) {
        dreamStopping = true;
        break;
    }
}

3.4.5 四个Task计算

这4个Task介绍如下:

prevTopRootTask:代表原始的获取焦点的Task。最顶层的rootTask,这个Task中有可能会包含多个子Task。

prevTopTask:获取最终显示在前台的那个Task任务,这个Task是子Task。

reusedTask:代表可重复使用的Task,如果为空则代表没有合适的Task(方法内会判断各种flag标记和启动模式),需要重新创建。

targetTask:如果reusedTask不为空,则就是reusedTask。否则重新通过computeTargetTask方法获取一个。

final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
final Task reusedTask = getReusableTask();
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();

接下来我们讲4个栈是如何获得的。

prevTopRootTask,直接通过当前显示区域,获取其根栈即可;

prevTopTask,如果prevTopRootTask不为空,则获取prevTopRootTask中的第一个即可。

reusedTask的获取逻辑稍微有一点复杂,这对应的其实也是5种启动模式的核心。这里我们简单介绍下,主要的流程就是判断如果是singeInstance的类型,则会从根容器中寻找对应的ActivityRecord对象并返回,否则从根容器中找到可复用的Task的头部ActivityRecord。

targetTask,则是最终使用的Task。如果reusedTask不为空,则说明找到了复用的栈直接使用。否则,通过computeTargetTask方法去寻找新的或者构造一个新的Task,这里仍然可能为空,比如singeInstance类型就找不到合适的Task。

class ActivityStarter{
    int startActivityInner(){
        ...
        final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
        ...
    }
    
    private Task computeTargetTask() {
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            return null;
        } else if(mSourceRecord != null){
            return mSourceRecord.getTask();
        } else if (mInTask != null) {
            ...
        } else {
            final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */, mOptions);
            final ActivityRecord top = rootTask.getTopNonFinishingActivity();
            if (top != null) {
                return top.getTask();
            } else {
                rootTask.removeIfPossible("computeTargetTask");
            }
        }
        return null;
    }
}

class RootWindowContainer {
    Task getOrCreateRootTask(){
        ...
        return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
            launchParams, launchFlags, activityType, onTop);
    }
}

class TaskDisplayArea {
    //此时 candidateTask = null,sourceTask = null,onTop = true,activityType应用的启动类型,有经典模型/桌面/最近任务等六种
    Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,Task candidateTask,Task sourceTask){
        
    }
}

computeTargetTask中的逻辑我们就不细讲了,简单来说,就是去寻找匹配的可复用的targetTask,如果找不到,则返回的就是null,就代表需要创建一个的Task。

3.4.6 检查是否允许生成新Task

如果需要新建Task,则检查是否有权限检查生成新task

int startResult = isAllowedToStart(r, newTask, targetTask);
if (startResult != START_SUCCESS) {
    if (r.resultTo != null) {
        r.resultTo.sendResult(INVALID_UID, r.resultWho, r.requestCode, RESULT_CANCELED,
                null /* data */, null /* dataGrants */);
    }
    return startResult;
}

int isAllowedToStart(ActivityRecord r, boolean newTask, Task targetTask) {
    ...
}

isAllowedToStart中进行一些合法性校验,判断是否允许创建新的Task。

比如桌面类型就不允许新建Task,后台进程如果没有权限也不允许新建。

如果不允许,则startResult!=START_SUCCESS,这时候通过sendResult通知响应Activity创建失败。

而权限检查通过后,有两块不同的分支逻辑 :

final ActivityRecord targetTaskTop = newTask
        ? null : targetTask.getTopNonFinishingActivity();
if (targetTaskTop != null) {
    // Recycle the target task for this launch.
    //第一块复用Activity逻辑
    startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants);
    if (startResult != START_SUCCESS) {
        return startResult;
    }
} else {
    //第二块复用Task逻辑
    mAddingToTask = true;
}

第一块,尝试寻找可复用的Task和Activity,如果找到了可复用Task和Activity,则进行复用,此时startResult!=START_SUCCESS,从而结束掉启动流程。

第二块,没有找到可复用Task和Activity,又分两种场景。如果找到了可复用Task但没有Activity,则mAddingToTask=true。否则mAddingToTask=true。具体的逻辑也是recycleTask中实现的。

3.4.7 复用Task

recycleTask中代码如下,主要有5块逻辑:

int recycleTask(Task targetTask, ActivityRecord targetTaskTop, Task reusedTask,
        NeededUriGrants intentGrants) {
    // Should not recycle task which is from a different user, just adding the starting
    // activity to the task.
    //逻辑1
    if (targetTask.mUserId != mStartActivity.mUserId) {
        mTargetRootTask = targetTask.getRootTask();
        mAddingToTask = true;
        return START_SUCCESS;
    }

    //逻辑2
    setTargetRootTaskIfNeeded(targetTaskTop);
    ...
    //逻辑3
    complyActivityFlags(targetTask,
            reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants);
    //逻辑4
    if (mAddingToTask) {
        return START_SUCCESS;
    }

    // The reusedActivity could be finishing, for example of starting an activity with
    // FLAG_ACTIVITY_CLEAR_TOP flag. In that case, use the top running activity in the
    // task instead.
    targetTaskTop = targetTaskTop.finishing
            ? targetTask.getTopNonFinishingActivity()
            : targetTaskTop;
    ...
    //逻辑5
    if (mMovedToFront) {
        // We moved the task to front, use starting window to hide initial drawn delay.
        targetTaskTop.showStartingWindow(true /* taskSwitch */);
    } else if (mDoResume) {
        // Make sure the root task and its belonging display are moved to topmost.
        mTargetRootTask.moveToFront("intentActivityFound");
    }
    // We didn't do anything...  but it was needed (a.k.a., client don't use that intent!)
    // And for paranoia, make sure we have correctly resumed the top activity.
    resumeTargetRootTaskIfNeeded();

    mLastStartActivityRecord = targetTaskTop;
    //逻辑6
    return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
}

1.如果targetTask的userId和mStartActivity的不一致,说明选择的可复用Task和目标Activity不属于同一个应用,则这个Task自然是无法复用的,所以修改标记位mAddingToTask=true,并且返回;

2.明确前台应该展示哪个Task,这里会计算mMovedToFront的值,明确是否需要切换到前台。

3.complyActivityFlags方法中主要根据mLaunchFlags和启动类型,来进行判断,决定最终是否要新建Task还是复用,这块逻辑我们稍后仔细讲一讲。

4.如果complyActivityFlags方法或者更早之前判断需要创建新的Task,则直接返回。如果不需要创建新的Task,则此时targetTask的top位置的ActivityRecord一定是可复用的那个,比如singleTask的类型,上一步骤会把复用栈中更上层的其它ActivityRecord清空,则我们只需要把targetTask显示到前台。

5.如果需要把task切换到前台,则通过showStartingWindow方法显示切换动画。

6.返回START_TASK_TO_FRONT或者START_DELIVERED_TO_TOP。

接下来,我们详细介绍下complyActivityFlags中的逻辑,这一块和Activity的启动模式息息相关,相关代码如下:

private void complyActivityFlags(Task targetTask, ActivityRecord reusedActivity, NeededUriGrants intentGrants) {
    ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity();
    final boolean resetTask = reusedActivity != null && (mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0;
    if (resetTask) {
        targetTaskTop = mTargetRootTask.resetTaskIfNeeded(targetTaskTop, mStartActivity);
    }
    
    if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
    //第一个if判断
        ...
        targetTask.performClearTaskForReuse(true /* excludingTaskOverlay*/);
        targetTask.setIntent(mStartActivity);
        mAddingToTask = true;
        mIsTaskCleared = true;
    
    } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
            || isDocumentLaunchesIntoExisting(mLaunchFlags)
            || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK,
                    LAUNCH_SINGLE_INSTANCE_PER_TASK)) {
    //第二个if判断                
        final ActivityRecord clearTop = targetTask.performClearTop(mStartActivity,
                mLaunchFlags);
    
        if (clearTop != null && !clearTop.finishing) {
            if (clearTop.isRootOfTask()) {
                // Activity aliases may mean we use different intents for the top activity,
                // so make sure the task now has the identity of the new intent.
                clearTop.getTask().setIntent(mStartActivity);
            }
            deliverNewIntent(clearTop, intentGrants);
        } else {
            // A special case: we need to start the activity because it is not currently
            // running, and the caller has asked to clear the current task to have this
            // activity at the top.
            mAddingToTask = true;
            // Adding the new activity to the same embedded TF of the clear-top activity if
            // possible.
            if (clearTop != null && clearTop.getTaskFragment() != null
                    && clearTop.getTaskFragment().isEmbedded()) {
                mAddingToTaskFragment = clearTop.getTaskFragment();
            }
            if (targetTask.getRootTask() == null) {
                // Target root task got cleared when we all activities were removed above.
                // Go ahead and reset it.
                mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags,
                    null /* task */, mOptions);
                mTargetRootTask.addChild(targetTask, !mLaunchTaskBehind /* toTop */,
                        (mStartActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
            }
        }
    } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) == 0 && !mAddingToTask
            && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
    //第三个if判断
        final ActivityRecord act = targetTask.findActivityInHistory(mStartActivity.mActivityComponent);
        if (act != null) {
            final Task task = act.getTask();
            task.moveActivityToFrontLocked(act);
            act.updateOptionsLocked(mOptions);
            deliverNewIntent(act, intentGrants);
            act.getTaskFragment().clearLastPausedActivity();
        } else {
            mAddingToTask = true;
        }
    } else if (mStartActivity.mActivityComponent.equals(targetTask.realActivity)) {
    //第四个if判断
        if (targetTask == mInTask) {
            // In this case we are bringing up an existing activity from a recent task. We
            // dont need to add a new activity instance on top.
         //这里对应FLAG_ACTIVITY_SINGLE_TOP类型
        } else if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                        || LAUNCH_SINGLE_TOP == mLaunchMode)
                && targetTaskTop.mActivityComponent.equals(mStartActivity.mActivityComponent)
                && mStartActivity.resultTo == null) {
            // In this case the top activity on the task is the same as the one being launched,
            // so we take that as a request to bring the task to the foreground. If the top
            // activity in the task is the root activity, deliver this new intent to it if it
            // desires.
            if (targetTaskTop.isRootOfTask()) {
                targetTaskTop.getTask().setIntent(mStartActivity);
            }
            deliverNewIntent(targetTaskTop, intentGrants);
        } else if (!targetTask.isSameIntentFilter(mStartActivity)) {
            // In this case we are launching the root activity of the task, but with a
            // different intent. We should start a new instance on top.
            mAddingToTask = true;
        } else if (reusedActivity == null) {
            mAddingToTask = true;
        }
    } else if (!resetTask) {
    //第五个if判断
        //这里对应默认的standard类型
        mAddingToTask = true;
    } else if (!targetTask.rootWasReset) {
        // In this case we are launching into an existing task that has not yet been started
        // from its front door. The current task has been brought to the front. Ideally,
        // wed probably like to place this new task at the bottom of its root task, but thats a little hard to do with the current organization of the code so for now we will just drop it.
        targetTask.setIntent(mStartActivity);
    }
}

输入主要参数有2个:

targetTask:之前找到的可复用的Task;

reusedActivity:可复的Task中最顶部的ActivityRecord。

第一个if判断:如果mLaunchFlags中有FLAG_ACTIVITY_NEW_TASK或FLAG_ACTIVITY_CLEAR_TASK标记,则绑定mStartActivity和targetTask,然后设置mAddingToTask = true;

第二个if判断:如果mLaunchFlags中有FLAG_ACTIVITY_CLEAR_TOP,或者属于LAUNCH_SINGLE_INSTANCE/LAUNCH_SINGLE_TASK/LAUNCH_SINGLE_INSTANCE_PER_TASK三种类型之一,这三种类型,都是要清空mStartActivity之上所有的Activity,此时mStartActivity就是顶部的ActivityRecord了,则返回此时顶部的ActivityRecord。注意,此时顶部的clearTop和mStartActivity都对应同一个Activity,但是对象是两个。

如果栈顶不为空,并且没有被finish,会去调用deliverNewIntent方法,最终通知到被复用的栈顶Activity的onNewIntent()方法,实现复用流程。

反之,则应该把当前的mStartActivity加入到这个目标Task中。

第三个if判断,如果mLaunchFlags中有FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_REORDER_TO_FRONT标记,并且mAddingToTask=false。则寻找是否有这样的ActivityRecord,如果有则把栈挪到前台。

第四个if判断,如果Task中顶部ActivityRecord就是我们的目标,并且mLaunchFlags中带有FLAG_ACTIVITY_SINGLE_TOP标记或启动模式为LAUNCH_SINGLE_TOP,并且还不是forResult方法启动的,则就可以复用此时栈顶部的ActivityRecord了。复用逻辑和2中一样。否则,设置mAddingToTask=true。

第五个if判断,就是我们最经常使用的standard类型,仅复用task,设置mAddingToTask=true。至此5种启动类型都有对应的处理。

所以,这里总结一下:

如果找到了可复用的Task和ActivityReocrd对象,就会执行newIntent的流程。这时候,返回外层的startResult就是START_TASK_TO_FRONT/START_DELIVERED_TO_TOP,从而结束掉后续的启动流程。

如果只是找到可服用的Task,则mAddingToTask=true。

如果都没找到,则mAddingToTask=false。

3.4.8 新建或复用Task

首先需要确认targetRootTask。

class ActivityStarter {
    int startActivityInner(){
        ...
        if (mTargetRootTask == null) {
            //确认targetRootTask
            mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask, mOptions);
        }
        ...
    }
}
class RootWindowContainer{
    ...
    private Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task,
            ActivityOptions aOptions) {
        final boolean onTop =
                (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
        final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null;
        return mRootWindowContainer.getOrCreateRootTask(r, aOptions, task, sourceTask, onTop,
                mLaunchParams, launchFlags);
    }
    ...
}

通过的是getOrCreateRootTask方法。

如果有sourceRecord时,一般使用的是sourceRecord的rootTask。

但是如果是singleTask/singleInstance等类型时(对应mLaunchTaskBehind标记),则不复用。

然后有两块逻辑,如果newTask=true,说明要新建Task,则执行新建的逻辑。

否则,mAddingToTask=true时,代表需要把Activity关联到当前Task上。

if (newTask) {
    final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
            ? mSourceRecord.getTask() : null;
    setNewTask(taskToAffiliate);
} else if (mAddingToTask) {
    addOrReparentStartingActivity(targetTask, "adding to task");
}

我们先看第一块逻辑:mSourceRecord不为空并且mLaunchTaskBehind=true时,使用mSourceRecord的Task,传入setNewTask方法。首先,创建一个的task,如果传入值不为空的话,mStartActivity关联taskToAffiliate,其实最终是把mStartActivity归属的task和taskToAffiliate进行关联。

private void setNewTask(Task taskToAffiliate) {
    final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
    final Task task = mTargetRootTask.reuseOrCreateTask(
            mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
            mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
            mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
    task.mTransitionController.collectExistenceChange(task);
    addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");

    ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",
            mStartActivity, mStartActivity.getTask());

    if (taskToAffiliate != null) {
        mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
    }
}

class ActivityRercord{
    void setTaskToAffiliateWith(Task taskToAffiliateWith) {
        if (launchMode != LAUNCH_SINGLE_INSTANCE && launchMode != LAUNCH_SINGLE_TASK) {
            task.setTaskToAffiliateWith(taskToAffiliateWith);
        }
    }

在看第二块逻辑:

mAddingToTask=true代表要加入到当前的targetTask中。

虽然有多种if else的场景,但是其实逻辑都是一致的,都是找到可复用的那个task,赋值给newParent对象。然后把mStartActivity绑定到newParent中。

private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
    TaskFragment newParent = task;
    if (mInTaskFragment != null) {
        ...
    } else {
        TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
        if (candidateTf == null) {
            final ActivityRecord top = task.topRunningActivity(false /* focusableOnly */,
                    false /* includingEmbeddedTask */);
            if (top != null) {
                candidateTf = top.getTaskFragment();
            }
        }
        if (candidateTf != null && candidateTf.isEmbedded()
                && canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) {
            // Use the embedded TaskFragment of the top activity as the new parent if the
            // activity can be embedded.
            newParent = candidateTf;
        }
    }
    if (mStartActivity.getTaskFragment() == null
            || mStartActivity.getTaskFragment() == newParent) {
        newParent.addChild(mStartActivity, POSITION_TOP);
    } else {
        mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
    }
}

至此,目标mStartActivity一定关联到了task和rootTask。

3.4.9 修改栈位置/显示启动动画

主要是startActivityLocked方法来实现这些逻辑。

void startActivityLocked(){
    Task rTask = r.getTask();
    ...
    //!r.mLaunchTaskBehind默认为true,allowMoveToFront默认也为true,newTask=true时代表没有找到可复用的栈
    if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) {
        positionChildAtTop(rTask);
    }
    /
    if (!newTask && isOrhasTask && !r.shouldBeVisible()) {
        ActivityOptions.abort(options);
        return;
    }
    ...
}

首先,newTask=true时代表没有找到可复用的栈,这时把r中关联的task挪到其所归属的rootTask最上层。

其次,如果不需要创建新的Task,则后面也不需要有什么切换动画。否则后面执行切换动画的逻辑。

3.4.10 收尾工作及进入启动流程

接下来做一些日志记录以及收尾的工作

  1. 修改应用级别的RootTask

  2. 加入到最新执行Task记录中

  3. 关联到rootTask上,如果没有,则创建rootTask。

  4. 逻辑4中的代码,我们下一小节来讲。

//ActivityStarter
int startActivityInner(){
    ...  
    //逻辑4. 这里的mDoResume一定为true,这里对应的启动逻辑,我们在3.4小节来讲。
    if(mDoResume){    
       final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();
       if (!mTargetRootTask.isTopActivityFocusable(){
          mTargetRootTask.ensureActivitiesVisible(...);
          mTargetRootTask.mDisplayContent.executeAppTransition();
       }else {
          mRootWindowContainer.resumeFocusedTasksTopActivities(
          mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
       }
    }
    
    //逻辑1       
    mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);
    //逻辑2     
    mSupervisor.mRecentTasks.add(startedTask);
    //逻辑3
    if (mOptions != null && mOptions.isLaunchIntoPip()
            && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()) {
        mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity, sourceRecord, "launch-into-pip");
    }
}

3.4 把Activity切到前台

上面说到,startActivityUnchecked中会进行启动Activity的操作,但是其实具体来说,分为三种启动情况。

  1. targetTask不在前台,这时候,就需要把栈切换到前台,我们3.4.2中介绍;

  2. targetTask在前台时,Activity不需要创建,我们3.4.3中介绍;

  3. targetTask在前台时,Activity需要创建,我们3.4.4中介绍;

我们一一来讲。

3.4.1 启动类型判断

上面有介绍,正常的启动流程中,mDoResume一定为true。

首先进行条件判断,满足以下几个条件则为true:

1.targetRootTask中栈顶的Activity是否获取到了焦点;

2.task中顶部Activity并不是mStartActivity,并且Task被覆盖。

如果以上条件不满足,则说明Task已经在前台显示了,则只需要切换mStartActivity到前台即可,则执行3.4.2的逻辑。

如果以上条件满足,说明Task并未在前台显示,则需要让rootTask显示在前台,执行3.4.3的逻辑。

相关代码如下:

if (mDoResume) {
    final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();
    if (!mTargetRootTask.isTopActivityFocusable()
            || (topTaskActivity != null && topTaskActivity.isTaskOverlay()
            && mStartActivity != topTaskActivity)) {
        //拉起目标Task,3.4.3中讲
        mTargetRootTask.ensureActivitiesVisible(null);
        mTargetRootTask.mDisplayContent.executeAppTransition();
    } else {
        if (mTargetRootTask.isTopActivityFocusable()
                && !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
            mTargetRootTask.moveToFront("startActivityInner");
        }
        //拉起目标Activity,3.4.2中讲
        mRootWindowContainer.resumeFocusedTasksTopActivities(
                mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
    }
}

3.4.2 拉起目标栈

3.4.1我们讲到,拉起目标栈的话,会通过下面两行代码来执行,我们来分别解释下。

mTargetRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);
mTargetRootTask.mDisplayContent.executeAppTransition();

首先,通过ensureActivitiesVisible方法,确保Activity的可见性。

mTargetRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);

mTargetRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);

然后调用DisplayContent的executeAppTransition方法,去进行栈的显示转换,代码如下:

void executeAppTransition() {
    mTransitionController.setReady(this);
    if (mAppTransition.isTransitionSet()) {
        ...
        mAppTransition.setReady();
        mWmService.mWindowPlacerLocked.requestTraversal();
    }
}

栈的显示状态转换完成,则栈顶的Activity也就会被显示出来了。

3.4.3 拉起目标Activity

如果mTargetRootTask获取到了焦点,但是栈不在前台的话,则把对应的栈挪到前台。

if (mTargetRootTask.isTopActivityFocusable()
        && !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
    mTargetRootTask.moveToFront("startActivityInner");
}
//开始拉启Avtivity的流程
mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);

接下来就是正式拉起Activity的流程了,对应方法为:resumeFocusedTasksTopActivities。

输入参数有四个,分别为:

变量名

类型

介绍

targetRootTask

Task

目标Activity所归属的栈

target

ActivityRecord

目标Activity,也就是ActivityStart中的变量startActivity

targetOptions

ActivityOptions

启动配置参数

deferPause

boolean

是否延时暂停

相关流程代码如下:

class RootWindowContainer{
    boolean resumeFocusedTasksTopActivities(Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,boolean deferPause) {
        boolean result = false;
        //逻辑1,通知到Task中的resumeTopActivityUncheckedLocked方法
        if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea() || getTopDisplayFocusedRootTask() == targetRootTask)) {
            result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions,deferPause);
        }
        ...
    }
}
class Task{
    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options, boolean deferPause) {
        if (isLeafTask()) {
            //逻辑3,调用到resumeTopActivityInnerLocked方法
            if (isFocusableAndVisible()) {
                someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause);
            } 
         }else {
            //逻辑2,遍历子Task
            int idx = mChildren.size() - 1;
            while (idx >= 0) {
                final Task child = (Task) getChildAt(idx--);
                ...
                someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options,
                        deferPause);
                // Doing so in order to prevent IndexOOB since hierarchy might changes while
                // resuming activities, for example dismissing split-screen while starting
                // non-resizeable activity.
                if (idx >= mChildren.size()) {
                    idx = mChildren.size() - 1;
                }
            }
        }
    }
}

逻辑1:RootWindowContainer的resumeFocusedTasksTopActivities方法中,会通知到Task中的resumeTopActivityUncheckedLocked方法,参数一致;

逻辑2:第一次调用这个方法时,肯定是rootTask对象,所以会遍历所有中leafTask,继续执行这个方法。

逻辑3:第二次调用的时候,就是leafTask对象了,此时就会调用到resumeTopActivityInnerLocked方法。

接下来,使用TaskFragment去启动目标Activity。

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ...) {
    final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */);
    ...
    final TaskFragment topFragment = topActivity.getTaskFragment();
    resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
}

resumeTopActivity方法中,我们把逻辑分为两大块,首先是第一块:

class TaskFragment extends WindowContainer<WindowContainer> {
    final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, boolean deferPause) {
        //获取当前显示区域
        final TaskDisplayArea taskDisplayArea = getDisplayArea();
        //逻辑1
        if (mResumedActivity == next && next.isState(RESUMED){
            return false;
        }
        //逻辑2
        final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
        if (!allPausedComplete) {
            return false;
        }
        //逻辑3
        boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
        //逻辑4
        if (mResumedActivity != null) {
            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity);
            pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,
                    next, "resumeTopActivity");
        }
        ...
        
    }
}

总结一下完成以下几项功能:

1.如果当前将要启动的next对象就是正在启动的mResumedActivity对象,则直接返回。

2.如果还存在正在pause的任务,则继续等待。

3.如果不延迟pause,则pause当前显示区域中的Activity,简单来说就是跨栈启动时,pause上一个Task中的Activity。

4.当前栈不为空时,则会存在一个之前栈顶显示的Activity对象resumeAcitivity,为了方便理解,我们暂且称之为LastActivity。此时resumeAcitivity就会被赋值给puaseActivity,然后会通知APP一侧暂停LastActivity对象,最后把系统侧LastActivity设置为PAUSEING状态。

接下来会进入到第二段启动逻辑的代码:

class TaskFragment extends WindowContainer<WindowContainer> {
    final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, boolean deferPause) {
        ...
        //展示启动动画
        if (anim) {
            next.applyOptionsAnimation();
        } else {
            next.abortAndClearOptionsAnimation();
        }
        
        //1.这里是resumeTopActivity中的第二段逻辑
        if (next.attachedToProcess()) {  
                final ClientTransaction transaction =
                        ClientTransaction.obtain(next.app.getThread(), next.token);
                // Deliver all pending results.
                ArrayList<ResultInfo> a = next.results;
                if (a != null) {
                    final int size = a.size();
                    if (!next.finishing && size > 0) {
                        if (DEBUG_RESULTS) {
                            Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a);
                        }
                        transaction.addCallback(ActivityResultItem.obtain(a));
                    }
                }
            
                if (next.newIntents != null) {
                    transaction.addCallback(
                            NewIntentItem.obtain(next.newIntents, true /* resume */));
                }
            
                // Well the app will no longer be stopped.
                // Clear app token stopped state in window manager if needed.
                next.notifyAppResumed(next.stopped);
            
                EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
                        next.getTask().mTaskId, next.shortComponentName);
            
                mAtmService.getAppWarningsLocked().onResumeActivity(next);
                next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
                next.abortAndClearOptionsAnimation();
                transaction.setLifecycleStateRequest(
                        ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                dc.isNextTransitionForward()));
                mAtmService.getLifecycleManager().scheduleTransaction(transaction);
        } else {
            //2.创建进程及Activity的流程
            mTaskSupervisor.startSpecificActivity(next, true, true);
        }
    }
}

1.首先,展示启动动画;

2.然后判断ActivityRecord是否绑定进程,这时候如果ActivityRecord是复用的,则构建ResumeActivityItem类型生命周期事务,然后通过binder对象通知APP去执行对应的生命周期流程,这一块第四章介绍。

3.如果ActivityRecord是新建的,则没有绑定进程。此时,则会通过startSpecificActivity方法去尝试创建Activity,我们3.4.4中详细介绍

3.4.4 新建目标Activity

上面讲到,如果是新建Activity,则会执行startSpecificActivity方法,我们来看下这个方法:


class ActivityTaskSupervisor{
    void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
        final WindowProcessController wpc = mService.getProcessController(r.processName, r.info.applicationInfo.uid);
        if (wpc != null && wpc.hasThread()) {
            realStartActivityLocked(r, wpc, andResume, checkConfig);
            return;
        }
        mService.startProcessAsync();
    }
}

这又分成了两条分支:

1.如果进程都不存在,则优先创建进程,走进程创建+Activity启动的流程。这块逻辑同应用启动流程,具体流程可以参考下面这篇文章,这里就不扩展了。android源码学习- APP启动流程(android12源码)

2.如果Activity对应的进程存在,则通过realStartActivityLocked方法走新建Activity的流程。我们来看下这个方法。

class ActivityTaskSupervisor{
    boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,        boolean andResume, boolean checkConfig) throws RemoteException {
        
        //绑定进程
        r.setProcess(proc);
        ...
        //构建事务
        final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.token);
        //添加创建Activity的事务到回调队列中
        clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),...);
        //添加生命周期事务
        final ActivityLifecycleItem lifecycleItem;
        //这个流程中,andResume一定为true。
        if (andResume) {
            lifecycleItem = ResumeActivityItem.obtain(isTransitionForward);
        } else {
            lifecycleItem = PauseActivityItem.obtain();
        }
        clientTransaction.setLifecycleStateRequest(lifecycleItem);
        //通过binder方法通知APP进程
        mService.getLifecycleManager().scheduleTransaction(clientTransaction);
        
        ...
    }
}

总结一下,首先让ActivityRecord绑定进程,然后构建Resume流程生命周期事务,并且添加LaunchActivityItem事务,最终通过binder方法通知到APP侧。这里的具体通知逻辑,仍然参看第四章。

3.5 五种启动类型原理总结

五种启动类型介绍:Activity的5种启动类型

我们经常说Activity有五种启动类型,其实对应的逻辑也就在这一章。这五种只是基础的类型,配合flag启动参数,其不同的类型则更多。这里,我们稍稍作一个五种类型的总结。

其实核心就是mAddingToTask的属性值。如果找到了可复用activity,则mAddingToTask=false,此时就会通知Acitivity进行复用,并且中断后序流程。

而如果没有找到可复用的activity,则mAddingToTask=true,则不会中断后续流程,就会执行Activity关联Task以及栈切换操作。

private void complyActivityFlags(Task targetTask, ActivityRecord reusedActivity,...){
    if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
        
    } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
            || isDocumentLaunchesIntoExisting(mLaunchFlags)
            || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK,
                    LAUNCH_SINGLE_INSTANCE_PER_TASK)) {、
         //singleInstance/singleTask/singleInstancePerTask类型 
         final ActivityRecord clearTop = targetTask.performClearTop(mStartActivity,mLaunchFlags);           
        if (clearTop != null && !clearTop.finishing) {
            deliverNewIntent(clearTop, intentGrants);
        } else {
            mAddingToTask = true;
            ...
        }
    } else if (mStartActivity.mActivityComponent.equals(targetTask.realActivity)) {
        if (targetTask == mInTask) {
           
        //singleTop类型
        } else if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                        || LAUNCH_SINGLE_TOP == mLaunchMode)
                && targetTaskTop.mActivityComponent.equals(mStartActivity.mActivityComponent)
                && mStartActivity.resultTo == null) {
            
            if (targetTaskTop.isRootOfTask()) {
                targetTaskTop.getTask().setIntent(mStartActivity);
            }
            deliverNewIntent(targetTaskTop, intentGrants);
        } else if (reusedActivity == null) {
            mAddingToTask = true;
        }          
    } else if (!resetTask) {
        //standard类型
        mAddingToTask = true;
    }
}

如上面的代码所示,如果是singleInstance/singleTask/singleInstancePerTask三种类型,如果栈中存在目标Activity,则清理targetTask之上的Activity。如果Activity没有finish,则调用deliverNewIntent走复用流程。否则设置mAddingToTask=true,仍然走绑定Task流程。

如果是singleTop类型,并且Task顶部的targetTaskTop等于目标Activity,则调用deliverNewIntent走复用流程。否则,设置mAddingToTask=true,仍然走绑定Task流程。

最后,!resetTask时,对应的其实就是我们最常使用的standard类型(前提flags中不设置reset标记),直接设置mAddingToTask=true走绑定Task流程。

3.6 startForResult的原理

Activity中startForResult的原理分析

四.系统通过事务通知APP的启动流程

这个单独整理成一篇文章进行讲解:

Activity事务管理ClientLifecycleManager原理讲解

五.扩展性问题

1.Activity启动失败,该怎么处理?

如文本2.2.2中的介绍,最好的方式就是参考失败code,来判断失败的原因。

比如通过后台service启动Activity,就会提示以下的错误:Abort background activity starts from 10235

我们通过对启动流程的分析,知道属于3.3.6小节的逻辑,知道高版本安卓对于后台启动Activity有限制,但是我们只要申请对应的权限(比如具有SystemAlertWindowPermission权限)即可绕过限制。

2.很经典的一个问题,ActivityA跳转ActivityB,整个生命周期流程是怎样的。

这个其实对应的就是本文的所有内容了。这个要区分ActivityB是否已存在。

如果不存在,则周期为:A.onPause()->B.onCreate()->B.onStart()->B.onResume()->A.onStop()。

如果存在,则要看具体的场景了。

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

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

相关文章

【五一创作】VS+Qt主界面内嵌自定义控件的四种方法以及不同自定义控件数据交互

前言 在Qt界面开发过程中&#xff0c;一个主界面或者主窗口看成是各个控件排列组合后的集合&#xff0c;对于一些项目而言&#xff0c;有些常用的控件可以封装成自己想要的控件样式并且复用&#xff0c;比如说&#xff0c;log显示控件&#xff0c;图像/视频显示控件等&#xf…

【ros2】ros melodic迁移到ros2 dashing过程中碰到的问题及解决方法

序言 总结踩坑经历&#xff0c;以利他人 1. error: forming pointer to reference type … & 报错原因&#xff1a; ros2回调函数的参数不能是引用形式 &&#xff0c;需要去除& 解决方法&#xff1a; 如果是指针引用&#xff0c;直接去除引用 void Callback(con…

【Java开发】Spring Cloud 11:Gateway 配置 ssl 证书(https、http、域名访问)

最近研究给微服务项目配置 ssl 证书&#xff0c;如此才可以对接微信小程序&#xff08;需要使用 https 请求&#xff09;。传统单体项目来说&#xff0c;首先往项目中添加证书文件&#xff0c;然后在配置文件中配置 ssl 证书路径、密码等相关信息&#xff1b;那么微服务这么多项…

机器学习强基计划8-5:图解局部线性嵌入LLE算法(附Python实现)

目录 0 写在前面1 流形学习2 局部线性嵌入算法2.1 什么是局部线性嵌入&#xff1f;2.2 算法原理推导 3 Python实现3.1 算法流程3.2 核心代码3.3 可视化 0 写在前面 机器学习强基计划聚焦深度和广度&#xff0c;加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的…

基于学生成绩管理系统(附源代码及数据库)

基于Ecplise&#xff0c;jsp的学生成绩管理系统 目录 登录页面 系统主页 管理员账号管理 学生查询 课程管理 成绩管理 后台数据库 源代码下载&#xff08;含数据库&#xff09; 毕设项目专栏 分为以下四大板块&#xff1a; 系统用户管理: 包含管理员账号管理&#…

【一起撸个DL框架】5 实现:自适应线性单元

CSDN个人主页&#xff1a;清风莫追欢迎关注本专栏&#xff1a;《一起撸个DL框架》GitHub获取源码&#xff1a;https://github.com/flying-forever/OurDL 文章目录 5 实现&#xff1a;自适应线性单元&#x1f347;1 简介2 损失函数2.1 梯度下降法2.2 补充 3 整理项目结构4 损失函…

第二十七章 Unity碰撞体Collision(下)

本章节我们继续研究碰撞体&#xff0c;并且探索一下碰撞体与刚体之间的联系。我们回到之前的工程&#xff0c;然后给我们的紫色球体Sphere1也添加一个刚体组件。如下所示 此时&#xff0c;两个球体都具备了碰撞体和刚体组件。接下来&#xff0c;我们Play运行查看效果 我们发现&…

第二十六章 Unity碰撞体Collision(上)

在游戏世界中&#xff0c;游戏物体之间的交互都是通过“碰撞接触”来进行交互的。例如&#xff0c;攻击怪物则是主角与怪物的碰撞&#xff0c;触发机关则是主角与机关的碰撞。在DirectX课程中&#xff0c;我们也大致介绍过有关碰撞检测的内容。游戏世界中的3D模型的形状是非常复…

浅谈区块链1.0-比特币

1. 比特币解决的问题 高度自治&#xff1a;国际经济危机无国界贸易&#xff1a;不同国家进行的贸易或者不同平台进行贸易 不可窜改&#xff1a;例如银行交易可能会被窜改数据 隐私安全&#xff1a;传统汇款方式会暴露你的个人信息&#xff0c;一旦数据库被别人入侵&#xff0c…

android基础知识复习

架构&#xff1a; 应用框架层&#xff08;Java API Framework&#xff09;所提供的主要组件&#xff1a; 名称功能描述Activity Manager&#xff08;活动管理器&#xff09;管理各个应用程序生命周期&#xff0c;以及常用的导航回退功能Location Manager&#xff08;位置管理器…

SpringBoot整合Mybatis-plus实现多级评论

在本文中&#xff0c;我们将介绍如何使用SpringBoot整合Mybatis-plus实现多级评论功能。同时&#xff0c;本文还将提供数据库的设计和详细的后端代码&#xff0c;前端界面使用Vue2。 数据库设计 本文的多级评论功能将采用MySQL数据库实现&#xff0c;下面是数据库的设计&…

Boonz-KeygenMe#1(★★★)

运行程序 错误&#xff1a; 查壳 没有壳&#xff0c;是汇编写的程序 载入OD 前面是在读取输入内容&#xff0c;到这里开始做计算了 分析 首先遍历了用户名&#xff0c;计算结果保存在EBX&#xff0c;在存放到 0x40E0F8 对EBX中的值再次计算&#xff0c;最后结果保存到 …

JavaFX: Java音乐播放读取歌词

JavaFX: Java音乐播放读取歌词 1、lrc歌词文件2、解析lrc歌词2.1 读取每行歌词2.2 解析歌词时间标签Time-tag2.3 解析歌词标识标签ID-tags2.4 创建对象包含歌词相关信息 3、播放显示歌词** 相关文献 JavaFX: Java音乐播放 1、lrc歌词文件 lrc歌词文件的扩展名 1、标准格式&a…

图像处理:Retinex算法

目录 前言 概念介绍 Retinex算法理论 单尺度Retinex&#xff08;SSR&#xff09; 多尺度Retinex&#xff08;MSR&#xff09; 多尺度自适应增益Retinex&#xff08;MSRCR&#xff09; Opencv实现Retinex算法 SSR算法 MCR算法 MSRCR算法 效果展示 总结 参考文章 前…

基频建模方法总结

基频F0建模方法 语音合成领域需要对基频进行建模&#xff0c;具体到文语转换TTS、语音转换VC、情感语音转换EVC领域等。 语音合成F0 包括文语转换&#xff0c;情感语音转换 TTEF&#xff1a;text-to-emotional-features synthesis EVC&#xff1a;emotional voice conversio…

这些你熟知的 app 和服务,都用上了人工智能

从微软在 Microsoft 365 服务中全面整合 GPT-4 能力 &#xff0c;让 PPT、Word 文档、Excel 表格的制作变成了「一句话的事」&#xff0c;到 Adobe 刚刚发布 Adobe Firefly模型集合&#xff0c;让图形设计、字体风格、视频渲染乃至 3D 建模的门槛显著降低——你我熟知的那些工…

idea的快捷键

一.idea的快捷键: 递进选择&#xff1a;ctrl w复制行&#xff1a;ctrl d删除行&#xff1a;ctrl y大小写切换&#xff1a;ctrl shift u展开/折叠&#xff1a;ctrl shift 减号/加号向前/向后&#xff1a;ctrl <— / —>Live Template(例如 输入psvm会自动打出mai…

华为OD机试题,用 Java 解【最远足迹】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不要…

Python实战项目:手势识别控制电脑音量

今天给大家带来一个OpenCV的实战小项目——手势识别控制电脑音量 先上个效果图&#xff1a; 通过大拇指和食指间的开合距离来调节电脑音量&#xff0c;即通过识别大拇指与食指这两个关键点之间的距离来控制电脑音量大小 技术交流 技术要学会分享、交流&#xff0c;不建议闭…

石英晶体振荡器【Multisim】【高频电子线路】

目录 一、实验目的与要求 二、实验仪器 三、实验内容与测试结果 1、观察输出波形&#xff0c;测量振荡频率和输出电压幅度 2、测量静态工作点的变化范围(IEQmin~IEQmax) 3、测量当静态工作点在上述范围时输出频率和输出电压的变化 4、测量负载变化对振荡频率和输出电压幅…