Android从点击Launcher图标开始到App打开流程分析
一、概述
从我们在屏幕上点击一个App快捷图标开始到app打开到启动页不过短短的500~1500毫秒之间。但是在这个过程的底层却做了非常多的事情,光大步骤就分了7步。其中AMS在这里面占据了非常重要的角色,基本上每一步都离不开他。
有些同学可能有疑问,了解这些东西有啥用啊?作用还是很大的,比如:我们了解了app的启动流程后就可以针对启动流程的原理做启动速度优化,了解了内部的Activity的启动原理后就可以做插件化实现热更新的同时还可以加深你对整个系统的理解,从而可以让编程的基本功更加扎实,不仅知其然还能知其所以然。不要怀疑,无论是开发中的方案设计、系统或者App优化都是先从原理了解的,主要指导了内部原理,就可以针对具体的问题,做针对性的优化。
下面具体讲一下从点击Launcher图标到app启动的原理,我们先看原理然后再看具体的源代码流程。
二、流程分析
我们经常看到的手机App图标页业界称之为Launcher,Launcher是一个Activity,只不过里面放置的是App启动图标。业界习惯上把这个Activity所在的进程称之为Launcher进程,下面在将的时候也按照业界标准来(为了更接地气)。Launcher中为每个App图标定义了启动这个App所需要的Intent信息,如:
action:android.intent.action.MAIN
category:android.intent.category.LAUNCHER
这些信息是App在安装或者系统启动的时候由PackageManagerService(PMS)从apk包中的AndroidMainifest.xml中解析出来的。
从点击Launcher图标到app启动经历了七大步。
1.Launcher通知AMS要启动某个App,并把图标中携带的启动信息也发给AMS。
2.AMS收到消息后保存App图标中携带的启动信息,并同坐App进程,我知道了,你可以休眠了。
3.Launcher进程收到AMS发过来的消息后,Launcher Activity进入休眠(pause),然后通知AMS我已休眠
4.AMS收到Launcher进程的休眠信息后,会检查目标App进程是否启动,如果启动就直接打开,如果没有启动就利用Proccess.start方法创建ActiivtyThread对象并执行其Main方法
5.App进程创建完成后会通知AMS App进程已创建完成,并把ActivityThread对象发送给AMS
6.AMS收到App进程发送过来的数据后会从第二步中翻出保存的App启动信息(具体要启动哪个Activity),然后通知app进程创建指定的Activity
7.App进程收到启动信息后,会根据启动信息创建Activity对象并调用其onCreate方法,然后创建Context对象并与Activity做关联。
到此,从点击Launcher到App启动的过程就完成了。
三、针对上面的七步,结合相关的类拆分开了进行分析。
第一步:Launcher进程通知AMS要启动某个Activity,并把启动信息发送给AMS。
Launcher所在Activity首先会调用自身的startActivitySafely方法,startActivitySafely方法又会调用Activity的startActivity方法,startActvity内部又会调用startActivityForResult方法。在startActivityForResult内部又会调用Instrumentation.execStartActiivty,execStartActivity又会调用ActivityManagerNative.getDefault().startActivity方法,把启动信息发送给AMS。
其中有两点需要注意:1.ActivityManagerNative.getDefault()方法的返回值是IActivityManger接口,其实现类是ActivityMangerProxy对象,即AMS在app中的代理对象。ActiivtyMangerNative和ActivityManagerProxy都实现了IActivityManager接口。IActivityManger接口中定义了app中四大组件的所有生命周期函数。
所以这里真正把Activity的启动信息发送给AMS是ActivityManagerProxy(AMS的代理对象)。
第二步:AMS收到消息后保存启动信息并通知Launcher进程进入pause状态
AMS通过App的代理对象ApplicationThreadProxy对象的schedulePauseActivity通知Launcher进程把LauncherActivity暂停。
ps: 在这个步骤中AMS会检查目标Activity是否在AndroidManifest.xml中注册了,如果没有注册就会提示抛异常,Activity not found。
第三步:Launcher进程收到AMS发过来的消息后,把LauncherActivity pause,并通知AMS已经暂停
Launcher进程的ApplicationThread对象收到消息后会调用ActivityThread的sendMessage方法向ActivityThread内部的H发送消息,H在接收到消息后会取出数据并调用自身的handleMessage方法,在handleMessage方法内部又会调用ActivityThread的handlePauseActivity方法把Activity方法暂停。
ps:在handlePauseActivity内部会调用ActivityThread的mActiviities集合并取出要pause的Activity进行pause,并通知AMS Activity已暂停。
第四部:AMS收到Launcher的已暂停消息后,会检查要启动的Activity所在的进程是否已经启动了,如果已经启动了就打开,如果没有启动就启动。
AMS会调用Process.start(android.app.ActivityThread)来启动一个新的进程
第五步:App进程启动好以后会通知AMS进程已经启动好了
AMS调用你Process.start方法会创建一个ActivityThread对象,并调用其main函数,然后再main函数内部会初始化主线程Looper即MainLooper,并创建Application对象。然后通过Instrumentation.newApplication通过反射创建Application对象,并创建ContextImpl通过Application的attach方法与Application进行绑定,最终会调用Instrumentation.callApplicationOnCreate执行Application的onCreate函数进行一些初始化的工作。完成后会通知AMS进程已经启动好了,并把ActivityThread对象发送给AMS
第六步:AMS收到app进程启动成功的消息后,通知App进程要启动哪个具体的Activity(在第二步中接收到的启动信息)
AMS把收到的ActivityThread对象包装成为ApplicatonThreadProxy对象,并调用其scheduleLauncherActivity方法,把具体要启动的Activity告诉App进程
第七部:App进程收到AMS具体要启动的信息后启动Activity并创建Context对象与Activity做关联。
app进程的ApplicationThread收到消息后会调用ActiivtyThread的sendMessage方法,给H发送消息,H收到消息后会调用自身的handleMessage方法,在handleMessage方法的内部又会调用ActivityThread的handleLaunchActivity方法,handleLauncherActivity方法的内部又会调用performLaunchActivity方法创建activity和context并将其做关联。
ps:在performLaunchActiivty的内部会首先通过Instrumentation.newActiviity方法通过反射创建Actiivty实例,然后创建Context对象并通过Activity的attach方法把Actiivty和Context绑定到一块。然后调用Instrumentation.callActiivtyOnCreate函数把执行Actiivty的onCreate函数。
到此,整个流程就分析完成了。
总结:以上的流程其实经历了三个进程Launcher进程、SystemServer进程(AMS所在的进程)和App进程。前三步都是Launcher进程和AMS进行通信,后四步都是AMS和App进程进行通信。大家仔细理解一下上面的这些流程其实都是Activity反反复复的和AMS机型握手的过程(通过Binder进行的IPC过程)。
下面给出一个类的执行流程图:
上面的过程是三个进程间的通讯。如果打开同一个App中的Activity流程是类似的。举个栗子:ActivityA要求启动ActivityB会经历5步
1.ActiivtyA通知AMS要启动ActivityB,并把要启动的ActivityB的信息发送给AMS
2.AMS收到启动信息后保存记录,并通知ActivityA,你可以去休眠了
3.ActivityA收到消息后进入休眠状态并通知AMS,我已休眠
4.AMS收到ActivityA的休眠信息,取出保存的ActivityB的信息,并检查到ActiivtyA和ActivityB在同一个进程中,通知App启动ActivityB
5.App进程收到消息后启动ActivityB。流程结束。相关的类关系,和上面的非常类似,就不过多赘述了。
讲到这里顺便讲一下Context家族树,不多说,贴出一张图,大家就立马能够看明白。
ps:说明一下,Context是一个抽象类。ContextWrapper继承了Context类并实现了其方法。但是不管是Context还是ContextWrapper内部都没有做具体的工作,具体的工作都是交给ContextImpl来完成的.
例如ContextWrapper.startActivity
@Override public void startActivity(Intent intent) { mBase.startActivity(intent); }
mBase指的就是ContextImpl对象。另外要注意ContextImpl对开发是不可见的。