1、前言
本文不同于其它的Activity启动流程的文章:我们会基于Android最新的9.0代码分析Activity的启动流程,同时我们不会贴代码,我们会从偏架构方面切入来介绍Android框架中Activity的启动流程。整体的思路如下:
从前面一篇文章我们学习到Android底层是通过Binder来实现跨进程通信的,那么我们从一个应用的Activity调用它的startActivity来启动另一个应用的Activity,也必然少不了使用到跨进程通信了,也就是使用到Binder了。学习了上一篇文章《插件化基础之Binder和AIDL》为我们更好的理解Activity启动流程会非常有帮助。
2、Activity启动流程——过程视图分析
一个应用的Activity到另一个应用的Activity不是直接调用,而是通过ActivityManagerService(缩写为AMS)中转。也就是说一个Activity到另一个Activity的过程要经过Activity -> AMS -> Activity。
我们拆解一下先介绍Activity到AMS的过程视图:
2.1、Activity到AMS
我们看到一个很熟悉的东西——AIDL。通过前面的学习我们知道“Android为了使用面向对象的方式来实现跨进程通信而使用了AIDL”,这里的Activity和AMS通信就是使用了AIDL的方式通过Binder驱动来实现的。他们的类关系图(即逻辑视图)如下:
这里涉及到一个类:Instrumentation,Android instrumentation是Android系统里面的一套控制方法或者”钩子“。这些钩子可以在正常的生命周期(正常是由操作系统控制的)之外控制Android控件的运行,后面会详细介绍该类。
2.2、AMS到Activity
上面的图描述了AMS -> Activity的启动流程,从startActivityWait到Activity的onCreate生命周期被调用。
如果此时启动Activity在新的进程中,那么此时AMS会启动一个新的进程,并让ActivityThread运行在这个新的进程中,整体流程如下:
流程描述如下:
- AMS 通过zygote fork一个进程出来,并在新的进程里面调用app的入口方法(ActivityThread.main)
- ActivityThread启动后将自己的ApplicationThread对象的Binder引用通过AIDL接口的attachApplication方法传递给远程服务端的AMS。
- 服务端(AMS)通过app端传递过来的ApplicationThread的Binder引用,更新缓存的ProcessRecord对象
- 服务端(AMS)通过IApplicationThread的binder引用回调app客户端的bindApplication对象,并最终调用了Application的onCreate方法。
AMS调用app端的bindApplication方法也是通过AIDL实现的,具体的逻辑视图如下:
2.3、AMS到四大组件的进程判断流程如下
2.3.1、Activity新进程判断流程图
2.3.2、Service新进程判断流程图
其它2大组件的判断也是类似的,这里就不一一赘述。我们看到都是用到了AMS的startProcessLocked方法。startProcessLocked的流程在上面2.2节的进程创建流程图中已经描述,可以再回顾一遍。
3、关键类介绍
3.1、ActivityThread
ActivityThread,就是主线程,也就是UI线程,它是在App启动时创建的,它代表了App应用程序。ActivityThread里面有main函数,我们知道java程序的入口函数就是main函数,那么我们可以看到Android的main函数是在ActivityThread里面。
3.2、ApplicationThread
ApplicationThread是ActivityThread的内部类,ActivityThread与启动Activity有关,那么ApplicationThread就与启动Application有关了。它继承自IApplicationThread.Stub,是AMS与app端通信的AIDL服务端,负责接收来自AMS的消息并处理。
3.3、H
ApplicationThread接收到来自AMS的消息后,调用ActivityThread的sendMessage方法,向app的主线程消息队列发送一个消息,前面说过,ActivityThread就是主线程(UI线程)。发消息是通过一个名为H的Handler类完成的。
我们知道继承自Handler类的子类,就要实现handleMessage方法,里面有switch…case语句,处理各种各样的消息。由此也能预见,AMS给Activity发送的所有消息,以及给其它三大组件发送的所有消息,都从H这里经过。为什么要强调这一点呢?既然四大组件都走这条路,那么就可以从这里入手做插件化技术。
它的handleMessage代码如下:
3.4、Instrumentation
官方对Instrumentation的介绍
instrumentation can load both a test package and the application under test into the same process. Since the application components and their tests are in the same process, the tests can invoke methods in the components, and modify and examine fields in the components.
翻译
Instrumentation可以把测试包和目标测试应用加载到同一个进程中运行。既然各个控件和测试代码都运行在同一个进程中了,测试代码当然就可以调用这些控件的方法了,同时修改和验证这些控件的一些数据
Android instrumentation是Android系统里面的一套控制方法或者”钩子“。这些钩子可以在正常的生命周期(正常是由操作系统控制的)之外控制Android控件的运行,其实指的就是Instrumentation类提供的各种流程控制方法,下表展示了部分方法的对应关系
具体源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * Perform calling of an activity's {@link Activity#onCreate} * method. The default implementation simply calls through to that method. * @param activity The activity being created. * @param icicle The previously frozen state (or null) to pass through to * @param persistentState The previously persisted state (or null) */ public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) { prePerformCreate(activity); activity.performCreate(icicle, persistentState); postPerformCreate(activity); } |
activity.performCreate方法最终会调用activity的onCreate生命周期方法。所以这里就好比Instrumentation勾住了本应该系统调用的onCreate方法,然后由用户自己来控制勾住的这个方法什么时候执行。
3.4.1、Instrumentation的使用
Instrumentation是Android自带一个单元测试框架,不过虽然这么说,其对于大部分应用开发人员来讲,最大的作用反而是用于功能或UI测试。
3.4.1.1、获取Activity中的控件
1 2 3 4 5 6 7 8 |
public void testActivity() throws Exception { Intent intent = new Intent(); intent.setClassName("com.hustophone.sample", Sample.class.getName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activity = (Activity) getInstrumentation().startActivitySync(intent); text = (TextView) activity.findViewById(R.id.text1); button = (Button) activity.findViewById(R.id.button1); |
3.4.1.2、操作Activity中的控件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
getInstrumentation().runOnMainSync(new PerformClick(button)); ... /* * 模拟按钮点击 */ private class PerformClick implements Runnable { Button btn; public PerformClick(Button button) { btn = button; } public void run() { btn.performClick(); } } |
3.4.1.3、hook instrumentation 监控应用内application及activity的生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 |
MyInstrumentation ins = new MyInstrumentation(); Class cls = Class.forName("android.app.ActivityThread"); // ActivityThread被隐藏了,所以通过这种方式获得class对象 Method mthd = cls.getDeclaredMethod("currentActivityThread", (Class[]) null); // 获取当前ActivityThread对象引用 Object currentAT = mthd.invoke(null, (Object[]) null); Field mInstrumentation = currentAT.getClass().getDeclaredField("mInstrumentation"); mInstrumentation.setAccessible(true); mInstrumentation.set(currentAT, ins); // 修改ActivityThread.mInstrumentation值 |
4、Context家族
Activity、Service、Application其实是亲戚关系,如图所示:
Activity因为有了一层Theme,所以中间有个ContextThemeWrapper,相当于它是Service和Application的侄子。
ContextWrapper里面有一个Context类型的成员变量mBase,当然它实际的类型是ContextImpl。ContextWrapper只是一个包装类,没有任何具体的实现,真正的逻辑都在ContextImpl里面。
一个应用包含的Context个数=Service个数+Activity个数+1(Application类本身对应一个Context对象)。
应用程序中包含多个ContextImpl对象,而其内部的变量mPackageInfo指向同一个PackageInfo对象。
我们在调用startActivity方法时可以用Activity对象的startActivity,也可以用context的startActivity。通过前面章节的学习我们知道activity的startActivity最终是执行的mInstrumentation的execStartActivity方法。我们看一下ContextImpl的实现,我们发现它也是调用的mInstrumentation的execStartActivity方法:
5、总结
本文从Android架构的角度介绍了Activity的启动流程,其核心是基于Android的Binder的跨进程通信机制。这个启动过程中我们接触到了一些对后续学习插件化非常有帮助的类:ActivityThread,H,Instrumentation,ApplicationThread等。相信有了这些基础,我们能更轻松的揭开插件化神秘的面纱。