应用启动方式包括四种:Activity、Broadcast、Service、Provider四种安卓组件被唤醒和拉起时都可以造成进程启动。下面先主要以Activity启动为例来做个简要分析。
一、冷启动跳转新应用
1、 startActivity阶段
常见的Activity启动一般由startActivity发起,以桌面点击图库冷起为例,startActivity函数执行过程如下(冷启动执行reusedTask为空分支):
08-24 20:58:18.744 1982 7944 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.wtf.gallery3d/.app.MainActivity bnds=[48,1681][294,1992] (has extras)} from uid 10100
startActivity执行到系统侧时一般第一行日志输出的关键字是“START u”,u0代表要启动的页面是主用户0的,子用户就是u10、u11这种(子用户从10开始计算),该行日志还会打印发起请求的intent信息,表明所启动的组件目标,from uid代表发起startActivity的发起者的uid,可以根据命令adb shell ps -A|findstr u0_a100查询到uid是10100的进程是哪个,10100要转成u0_a100,a代表10000,如果是子用户进程就是u10_a100这种一次类推。如果该关键字日志不存在,则查看android源码对照Instrumentation.checkStartActivityResult函数中抛出的错误信息搜索判别是组件找不到、没权限启动、组件是应用私有未对外exported等具体原因,一般没有START u关键字就要从发起方和目标应用去排查。
在打印START u日志之后,ATMS这块会查找复用栈,由于冷起,所以没有复用栈,此时会新建ActivityRecord、ActivityStack(R版本开始全屏、悬浮窗等非分屏场景的ActivityStack就是作为Task在使用,二者是同一个对象,ActivityStack继承Task),这一点从以下dump信息也可以看出来:
创建完ActivityStack对象后会打印以下events日志,该日志表面栈id是3405:
08-24 20:58:18.759 1982 7944 I wm_stack_created: 3405
一个ActivityStack在构造时就会加到栈顶,此时并没有wm_focused_stack的events日志打印,因为创建完成后加到栈顶时传入的updateLastFocusedStackReason是null,不会造成events日志输出,而且即便紧接着还有一次moveToFront但由于栈顶对象还是刚才新创建的stack,所以也不会打印该events日志。
在将ActivityStack作为一个Task对象后,会打印以下events日志:
08-24 20:58:18.763 1982 7944 I wm_create_task: [0,3405]
注意,虽然前面很早就新建了ActivityRecord,但是wm_create_activity这个events日志并不是构造ActivityRecord时打印的,而是在wm_create_task之后才打印出来:
08-24 20:58:18.763 1982 7944 I wm_create_activity: [0,14228247,3405,com.wtf.gallery3d/.app.MainActivity,android.intent.action.MAIN,NULL,NULL,270532608]
在创建好各级数据结构并关联好上下级关系之后,才会真正去尝试resumeTopActivity去resume栈顶的ActivityRecord(此时栈顶就是要启动的图库应用),注意这个地方是尝试去resume,不代表真正能去resume成功,因为此时会判断是否有需要执行不可见的ActivityRecord要被pause,从桌面点击启动图库,显然需要把桌面pause掉,此时逻辑代码如下:
该处有两个pause尝试,pauseBackStacks代表要去把display上的其他栈尝试计算可见性判别是否需要pause,比如桌面点击图库跳转不同栈时,就需要执行这一步把桌面栈pause掉;而紧接着的startPausingLocked判断条件要求当前要resume的stack具有mResumedActivity,也就是说当前栈在前台可见了,只是栈内部有activity跳转,也就是说应用内部进行跳转执行这个pause。这两个pause动作无论执行哪一个都会有以下日志输出,当然这个仅仅是系统侧的分发pause的日志,还没到app侧执行onPause:
08-24 20:58:18.765 1982 7944 I wm_pause_activity: [0,225436362,com.wtf.launcher/.Launcher,userLeaving=true]
在发起对上一个应用也就是桌面的pause操作时,判断要resume的next也就是图库页面是没有进程存在的,需要新启动一下进程,此时会异步执行图库的进程拉起,至此startActivity函数就执行完毕了,其实可以看出来,startActivity此时执行了个寂寞,仅仅发起了对上一个应用的pause(异步,所有的生命周期调度到应用侧的都是oneway异步不等待的,参见IApplicationThread类带有oneway标记)和启动要拉起的页面的进程(异步),除此之外,别无他物。