Android 7.0 中 Launcher 启动 Activity 过程

话不多说,先看看整体跳转启动架构:

从图中,我们可以看到从Lanucher启动Activity会至少涉及3个进程,其中ActivityManagerService则是其中的纽带。

由于Activity的启动流程步骤经过了多层的封装,步骤较多。所以我先简单说明一下Framework层主要类的主要功能,然后在跳转步骤中重点说主要的。

主要类说明:

User进程

ApplicationThread:是一个Binder对象,里面有各种于AMS通信的接口,主要负责与AMS的通信。

ActivityThread:里面有进程主线程的入口,类似于main的功能,也建立main looper。同时负责与AMS的四大组建、生命周期的各种调度。

Instrumentation:可以理解为一个包装好的统一管理类,里面有callActivityOnCreate、callApplicationOnCreate、newActivity、callActivityOnNewIntent 等基本上在application和activity的所有生命周期调用中的方法。

AMS进程

ActivityManagerService:Activity管理机制的服务器端,属于一个系统服务。用于管理activity的各种行为,控制activity的生命周期,派发消息事件,低内存管理等等。实现了IBinder接口,可以用于进程间通信。

ActivityStack:为了让这许多 Activity协同工作而不至于产生混乱,Android平台设计了一种堆栈机制用于管理Activity,其遵循先进后出的原则,系统总是显示位于栈顶的Activity。

ActivityStackSupervisor:顾名思义,ActivityStack的管理者,宏观上把一群sleep、visible、stop的Activity汇集起来。也负责Activity生命周期的一些方法调度。

数据结构

ActivityRecord:Activity的一些信息,比如说宿主进程、component信息等。

ProcessRecord:有进程的一些信息,可能会包含这很多ActivityRecord(还有Service等其他组件)。

TaskRecord:task信息,不过有一点注意的是taskRecord对应的ActivityRecord会处于连续的位置。

接下来直接看代码吧

1、Activity.startActivity

2、Activity.startActivityForResult

3、Instrumentation.execStartActivity

上面步骤中,经过几步的方法包装后,startActivity会交给Instrumentation.execStartActivity方法。这个方法中,会把当前Activity的Binder对象ApplicationThread、包名、intent数据等一些信息通过ipc交给AMS处理。

4、ActivityManagerService.startActivity

5、ActivityManagerService.startActivityAsUser

6、ActivityStarter.startActivityMayWait

获取pid和uid以便权限检查

7、ActivityStarter..startActivityLocked

根据caller的ibinder获取ProcessRecord,不为空则重新赋值pid和uid

确定sourceRecord和resultRecord,sourceRecord代表请求启动当前activity;后者表示需要返回结果的ActivityRecord。一般,如果sourceRecord的activity使用startActivityForResult启动当前activity并且requestCode>=0时,则resultRecord=sourceRecord。

但是当intent设为FLAG_ACTIVITY_FORWARD_RESULT的时候,这个情况很特殊。比如说A打开B,B会把result给A,但是如果B打开C的时候设置了FLAG_ACTIVITY_FORWARD_RESULT,那C也会把result给A,这时候会冲突,就会把这个result直接干掉。

8.ActivityStackSupervisor.jcheckStartAnyActivityPermission

这几步

(1)首先是检测是否有root权限

(2)然后如果export=false,且callingUid不等于启动的uid(不在一个进程),则不予权限。

(3)activity的permission与application相同,则授权。

(4)activity的permission为空授权;

(5)请求启动的activity设定了permission,那么检查caller的activity中是否声明了使用这个permission,如果声明了授权。

回到7的startActivityLocked继续往下走

computeLaunchingTaskFlags

判断task是否为空,把flag设为New Task,不为空则判断标志位是否为New Task。

再通过Launchmodel设定的singleInstance和newtask真正确定launchflag。

在singleTop的时候,如果在top,直接通知其执行onNewIntent方法,对应应用层的onNewIntent

在前两步中确定的launchFlag为New task,mAddingToTask不为true等其他条件满足时,创建或复用task。

Task的复用值得一说:

如果一个activity启动时创建的了一个新的task,那么这个activity就是该task的root activity。而其中的几个属性affinity、intent是均指root activity的。

关于task的复用条件刚才已经提到了,为什么能去复用呢,因为我们知道以NewTask和NewInstance这两个launchmode的Activity只可能会是task的root Activity,既然是root,那么其affinity肯定是和task保持一致,这样就可以通过affinity来寻找。

而查找的过程是通过AMS中的mHistory来处理的

(1)查找mHistory中是否有与要启动的activity相同affinity的task,这点上面已经明述。

(2)如果activity的android:taskAffinity属性为空,此时AMS就会去mHistory中通过比较task.intent.getComponent()和启动activity的Comeponent比较,去查找是否存在task的root activity和启动的activity相同。

(3)如果task.Intent为空且这种情况发生在TaskReparenting之后,所谓TaskReparenting是用于设定Activity能够从启动它的任务中转移到另一个与启动它的任务有亲缘关系的任务中。如果设置了true,则能够转移,如果设置了false,则这个Activity必须要保留在启动它的那个任务中。TaskReparenting之后,AMS为TaskReparenting前的activity创建一个新的task,并将启动这个activity的Intent赋值给task.affinityIntent,并且此时的task.Intent==null。此时就需要比较task.affinityIntent.getComponent()和启动activity的Comeponent比较,看是否和启动的activity相同。

假如activity没获得焦点,无法进行resume操作。或者满足条件后开始resume。

9、ActivityStackSupervisor.resumeFocusedStackTopActivityLocked

10、ActivityStack.resumeTopActivityUncheckedLocked

在resumeTopActivityInnerLocked中先执行onpause,再考虑onresume。

先获取来源activity prev的Binder对象,通过ipc告诉prev该pause了。

11、ActivityThread.schedulePauseActivity

以上动作在Activity的进程中最终完成pause的操作。

在完成pause之后,通过ipc告诉AMS

12、AMS.activityPaused

13、ActivityStack.activityPausedLocked

14、ActivityStack.completePausedLocked

经过多层包装后定位到该方法。

15、ActivityStackSupervisor.startSpecificActivityLocked

如果process不在,就准备开启新的process,否则就直接准备launchActivity了,process在的情况其实是不在情况的一种子情况。

启动新的process有几个条件

(1)Process不存在

(2)该Process死亡,实际的caller也认为它真的死亡了,或者认为它有crash风险的时候会重新创建一个新的

(3)Pid没有被使用(也就是说避免重复启process)

而在启动的时候,会发出一个PROC_START_TIMEOUT的消息,这个消息意味这进程必须在这个消息触发之前处理,否则就认为它超时,也将无法和Activity组件关联起来。

16、ActivityThread.main

创建主线程和MainLooper,其在进程一启动的时候就诞生了。而ActivityThread在attach的时候会告诉系统这是非系统进程。Attach过程,会通过binder告诉AMS执行attachApplication,这才真正把binder对象(进程的代表)和pid进行绑定。

而在attachApplicationLocked中,在启动process之前,我们已经获得了一个和pid关联的processRecord这么一个对象,如果进程在上一步中提到的消息处理时间内中完成了,这一步就会进一步完善processRecord,也就是把进程创建完成后的那个ApplicationThread这个binder对象赋给processRecord,这样AMS就可以于创建的进程通信了。

上一步执行完后,AMS会告诉进程,可以启动Application了。先会会Application创建一个ApplicationContext,并初始化最开始的启动Activity的Instrumentation,之后Instrumentation会执行我们在Application常见的oncreate方法。

启动完了process,刚才还有个Activity等着我们去启动呢,就是这一步中在干的事。这一步android的四大组建都会在这里等着初始化。在这个方法中,先获取stack中top running Activity。什么情况下我们该启动它呢?显然:

如果发现这个Activity的包名、uid等于process的包名、uid。

这个Activity没有附着在任何process上。

这时候就可以在这个process中启动它。

17、AMS.scheduleLaunchActivity

17、ActivityThread.scheduleLaunchActivity

如果Activity为空,则创建一个新的Activity,并执行attach操作,这个操作就类似于构造方法给成员变量赋值,以便后续的操作。之后依次为Activity执行oncreate、onstart等操作。而在onresume操作中,ActivityThread又会通过Binder告诉AMS它已经进入onResume状态,至此整个activity的启动过程就结束了。

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏mukekeheart的iOS之旅

Android基础总结(8)——服务

服务(Service)是Android中实现程序后台运行的解决方案,它非常适合用于去执行哪些不需要和用户交互而且还要长期运行的任务。服务的运行不依赖任何用户界...

3658
来自专栏刘望舒

Android深入四大组件(七)Android8.0 根Activity启动过程(后篇)

前言 在几个月前我写了Android深入四大组件(一)应用程序启动过程这篇文章,它是基于Android 7.0的,当我开始阅读Android 8.0源码时发现应...

22910
来自专栏编程思想之路

Android6.0源码之蓝牙研究汇总(一)--from初学者

fang_fang_story 因为原先刚开始看蓝牙时比较匆忙,而且整个流程都不太懂,感觉遗漏了好多东西,打算从头分析,分析跟蓝牙相关的所有问题,所以如果对蓝...

24910
来自专栏函数式编程语言及工具

ScalaPB(1): using protobuf in akka

1713
来自专栏草根专栏

使用 C# (.NET Core) 实现命令设计模式 (Command Pattern)

本文的概念内容来自深入浅出设计模式一书. 项目需求 ? 有这样一个可编程的新型遥控器, 它有7个可编程插槽, 每个插槽可连接不同的家用电器设备. 每个插槽对应两...

3058
来自专栏10km的专栏

mysql/jdbc:设置useInformationSchema=true读取表注释信息(table_comment)

问题描述 今天在读取表的注释信息(COMMENT)时,发现返回的REMARKS字段返回居然是null. 以下是代码示例: DatabaseMetaData...

2057
来自专栏向治洪

保证service存活

Android开发的过程中,每次调用startService(Intent)的时候,都会调用该Service对象的onStartCommand(Intent,i...

1767
来自专栏Java与Android技术栈

Kotlin Coroutines 笔记 (二)

协程虽然是微线程,但是并不会和某一个特定的线程绑定,它可以在A线程中执行,并经过某一个时刻的挂起(suspend),等下次调度到恢复执行的时候,很可能会在B线程...

481
来自专栏闻道于事

DRUID控制

402
来自专栏技术墨客

Vert.x源码-创建集群 原

在当前的最新版本中,Vert.x官方只实现了利用Hazelcast来创建集群。当然,如果可以的话,也可以通过ClusterManager接口实现或引入需要的集群...

1193

扫码关注云+社区