Activity 启动流程分析

e06546596cfddb92835fdc1778b0563e.gif

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一、Android Acitvity启动流程概览
二、启动流程中AMS内的各个角色
三、AMS跨进程通信创建Activity,第一步
四、ActivityStarter 正式开始启动Activity
五、startActivityMayWait
六、startActivity处理ActivityInfo转化为ActivityRecord
七、小结

一、Android Acitvity启动流程概览

outside_default.png

注意:红色线代表跨越Binder一次进程

从时序图上,无论怎么Android的启动架构怎么演变,其根本流程都没有变。Android都是通过Binder通行到AMS,接着经过AMS的一系列中栈处理之后,把ActivityRecord返回到AppThread(App进程中)。

在Android 9.0中,AMS不再是通过简单的调用IPC来控制App端的Activity生命周期。而是通过一个状态设计模式,将每个Activity每一个生命周期都抽象成一个状态,接着通过状态机去管理整个生命周期。

提示:从上面几篇文章能看到,实际上AMS隶属于SystemServer进程。和App进程不在同一处。

二、启动流程中AMS内的各个角色

在Activity中启动中,AMS担任最为重要的角色,下面列出的都是AMS中承担各个主要功能的类

  • 1.ActivityStack 代表着Activity的栈(不精准稍后会具体解释)

  • 2.ActivityStarter 代表着Activity正式启动的控制类

  • 3.ActivityManagerService 代表着一切Activity行为在系统中的控制中心

  • 4.ActivityStackSupervisor 代表着ActivityStack的监控中心

实际上对于我们来说在整个Activity的启动需要关注这么四个核心类。
而在这里面往往涉及到Activity栈的变化,而这个过程涉及到的核心类有:

  • 1.ActivityRecord

  • 2.TaskRecord

  • 3.mRecentTasks

  • 4.mTaskHistory

  • 5.ProcessRecord

三、AMS跨进程通信创建Activity,第一步

public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
            boolean validateIncomingUser) {
        enforceNotIsolatedCaller("startActivity");

        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)//调用方的AppThread的IBinder
                .setCallingPackage(callingPackage)//调用方的包名
                .setResolvedType(resolvedType)//调用type
                .setResultTo(resultTo)//调用方的ActivityClientRecord的binder(实际上是AMS的ActivityRecord对应在App端的binder对象)
                .setResultWho(resultWho)//调用方的标示
                .setRequestCode(requestCode)//需要返回的requestCode
                .setStartFlags(startFlags)//启动标志位
                .setProfilerInfo(profilerInfo)//启动时带上的权限文件对象
                .setActivityOptions(bOptions)//ActivityOptions的Activity的启动项,在一般的App中此时是null,不需要关注
                .setMayWait(userId)//是否是同步打开Actvivity 默认一般是true
                .execute();//执行方法。
复制代码

从这里面节能很清晰的明白,在启动过程中需要什么参数。虽然看起来像是一个建造者设计模式,但是实际上工厂设计模式+享元设计+链式调用。通过obtainStarter把DefaultFactory从mStarterPool中获取一个ActivityStarter(池子中最多设置3个),接着通过链式调用,把启动时需要的参数传递进去。

当设置完成之后,我们直接看看execute方法。

四、ActivityStarter 正式开始启动Activity

int execute() {
        try {
            // TODO(b/64750076): Look into passing request directly to these methods to allow
            // for transactional diffs and preprocessing.
            if (mRequest.mayWait) {
                return startActivityMayWait(mRequest.caller, mRequest.callingUid,
                        mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                        mRequest.inTask, mRequest.reason,
                        mRequest.allowPendingRemoteAnimationRegistryLookup);
            } else {
           ...
            }
        } finally {
            onExecutionComplete();
        }
    }
复制代码

从execute我们可以看到,在这个过程Google工程师灵活的运用了try-final机制,通过onExecutionComplete在ActivityStartController清除数据放回startPool池子中。

此时我们是一个同步操作,所以看看startActivityMayWait方法。

五、startActivityMayWait

这段我们分为三段来看:

5.1.从PackageManagerService准备activity需要的数据

private int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult,
            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
            int userId, TaskRecord inTask, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup) {
      ....

        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                0 /* matchFlags */,
                        computeResolveFilterUid(
                                callingUid, realCallingUid, mRequest.filterCallingUid));
        if (rInfo == null) {
            UserInfo userInfo = mSupervisor.getUserInfo(userId);
            if (userInfo != null && userInfo.isManagedProfile()) {
                // Special case for managed profiles, if attempting to launch non-cryto aware
                // app in a locked managed profile from an unlocked parent allow it to resolve
                // as user will be sent via confirm credentials to unlock the profile.
                UserManager userManager = UserManager.get(mService.mContext);
                boolean profileLockedAndParentUnlockingOrUnlocked = false;
                long token = Binder.clearCallingIdentity();
                try {
                    UserInfo parent = userManager.getProfileParent(userId);
                    profileLockedAndParentUnlockingOrUnlocked = (parent != null)
                            && userManager.isUserUnlockingOrUnlocked(parent.id)
                            && !userManager.isUserUnlockingOrUnlocked(userId);
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
                if (profileLockedAndParentUnlockingOrUnlocked) {
                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                            PackageManager.MATCH_DIRECT_BOOT_AWARE
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                            computeResolveFilterUid(
                                    callingUid, realCallingUid, mRequest.filterCallingUid));
                }
            }
        }
        // Collect information about the target of the Intent.
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
复制代码

可以大致分为以下3步: 1.从ActivityStackSupervisor调用PMS获取ResolveInfo。

private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
            int flags, int userId, boolean resolveForStart, int filterCallingUid) {
        try {

            if (!sUserManager.exists(userId)) return null;
            final int callingUid = Binder.getCallingUid();
            flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart);
            mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                    false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");

            final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
                    flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/);

            final ResolveInfo bestChoice =
                    chooseBestActivity(intent, resolvedType, flags, query, userId);
            return bestChoice;
        } finally {
          ....
        }
    }
复制代码

从上面代码,我们可以看到,这个方法是通过intent来从找到一个最合适的选择。我们可以推测,实际上这个ResolveInfo是指当我们安装了App之后,加载到PackageManagerService(后面称PMS)系统中的AndroidManifest.xml的数据。

queryIntentActivitiesInternal分步骤来说:

  • 1.查看当前Intent是否是显式Intent。是则取出其中的class对象和AndroidManifest的进行匹配,匹配成功返回。

  • 2.如果没有指定包名则全系统的查找匹配intent

  • 3.如果指定包名,则从当前的包名寻找匹配规则相符合的intent的Activity

因此此时可能会匹配多个合适的Intent,再通过chooseBestActivity进一步筛选Activity。

为什么加上这一段,实际上这一段有一个关键的逻辑就是AppLink。开发经常用到,在AndroidManifest中设置好schme等Intent参数,让外部app来唤醒我们自己的app。 当唤醒的目的地只有一个直接返回,如果有多个则替换intent中的类,变成系统的ResolveActivity。用来选择我们的目的App,如下图。

[图片上传失败...(image-402965-1628853436643)]

  • 2.查不到ResolveInfo则尝试从直接启动中获取

自Android 5.0之后。Android系统将开始支持多用户系统,这些用户的配置都由UserManager控制,其中AccountManager则是控制每个用户下的账号。

在Android7.0之后,为应用新增了一种启动模式Direct Boot(直接启动模式)。这种模式是指设备启动后进入的一个新模式,直到用户解锁(unlock)设备此阶段结束。这种模式,会为程序创建Device protected storage私有的存储空间。

这种模式比较特殊,我们需要在AndroidManifest中设置 android:directBootAware="true"。

因此,这种模式下,需要唤醒特殊的Activity,确定此时已经解锁,需要从特殊的私有空间去查找对应的ResolveInfo。

  • 3.通过PMS的getActivity

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员Android

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值