Application、Activity、Service和Context之间的构建关系

一、Context与Application、Activity、Service的继承关系

       在开发过程中,经常会遇到使用context的情况,如通过context得到resource,通过context实现layoutInflater等,在代码环境中使用context,可用通过activity、application以及service来实现,那么这是为什么呢?

       因为三者都是继承自context抽象类的,如下图所示:

       可以看到,activity是继承自ContextThemeWrapper,而Service和Application继承自ContextWrapper

       对于ContextWrapper和ContextThemeWrapper而言,两者存在继承关系,最终继承自Context,而Context实际上是一个抽象类,它的实现是交给ContextImpl类负责,所以可以先提一下,app在启动时,application、activity和service三者能够关联到context,实际上都是在创建者三个要素的时候,同时实现了ContextImpl对象,具体证明放在之后描述。

      知道了activity、service和application与context的关系之后,也就理解了为什么可以把这仨当context来用了,其实context意义为场景,而activity、service及application从语义上理解也是场景的概念。

      那么还有一个问题,activity等着仨是如何实现各自context的对应关系呢,需要分析app的启动过程了。

二 、Context与三者的对应关系描述

       在app启动时,或者启动一个新的activity及service中,实际上先由AmS(Activity Manager Service)来负责,AmS在一个进程中,启动的Activity或app是由另一个所在进程ActivityThread来实现,AmS负责管理ActivityThread中的相关具体过程,因此需要实现跨进程间的数据交互,而AmS和ActivityThread的数据交互入口是ActivityThread中的ApplicationThread,ApplicationThread是一个Binder变量,可以接受AmS传递来的数据。

2.1 Application与Context的关系

       application初启动时,对于ActivityThread中,首先会执行到bindApplication方法,该方法的声明如下:

可以在参数列表中发现有一个ApplicationInfo类型的参数appInfo,该数据实际上是由AmS传递过来的,且ApplicationInfo类型实现了Parcelable接口。

       在调用了bindApplication方法之后,通过传递来的appInfo,会构建一个AppBindData类型的数据,该数据构建完成之后,会由ActivityThread的内部Handler发送一个消息:

此时,ActivityThread由于实现了Handler的handleMessage,所以在收到消息为BIND_APPLICATION时会回调handleBindApplication方法,在该方法中可以找到创建Application的过程:
可以看到一个关键方法data.info.makeApplication()

        这里的data就是之前创建完成的AppBindData类型的,也是handleBindApplication的传入参数;info是LoadApk类型,在老版本中info实际上就是PackageInfo这个类,所以LoadApk就是PackageInfo,LoadApk这个类中有makeApplication方法,在该方法中存在实现context和application关联的步骤,查看makeApplication方法:

在makeApplication方法中,创建了ContextImpl对象,并在ActivityThread中,通过Instrumentation对象创建了app,然后将app设置为context的外在体现,即setOuterContext方法,该方法如下:
该方法接收一个context对象,把其设置成mOuterContext,其实就是代言人的概念了。

       以上就实现了application的创建过程中实现context关联的过程,再由流程图的形式描述一下:

2.2 Activity与Context的关系

        activity与context的关系也类似与application的流程,启动activity时,首先也是交由AmS进行处理,AmS会传递一个ActivityInfo类型的数据给ActivityThread,然后ActivityThread拿到了该数据之后执行后续的一系列操作。ActivityInfo也是一个实现Parcelable接口的类型。

ScheduleLaunchActivity这个方法相当于在接收到AmS传递来的ActivityInfo之后,执行的预处理工作,这个预处理主要是updateProcessState和构建ActivityClientRecord对象,完成之后sendMessage
看到了Activity是如何明面上创建的了,接下来执行到performLaunchActivity方法中:
从上面的代码中就了解了Activity对象是如何创建的,以及Context对象是如何关联到activity的,可以看看createBaseContextForActivity这个方法的具体实现:
可以看到在该方法中,同样通过ContextImpl的静态方法createActivityContext来创建ContextImpl对象,然后通过setOuterContext来设置activity为context的外部代言人

       整体流程如下:

2.3 Service与Context的关系

        与上述两种情况类似,启动Service(注意这里是startService而不是bindService)时,首先AmS进行处理,包装出一个ServiceInfo类型的数据,ActivityThread接收到数据后首先执行scheduleCreateService方法:

创建了CreateServiceData之后发送消息CREATE_SERVICE,然后得到handleMessage的响应,进入到handleCreateService方法中:

       在该方法中,首先通过getPackageInfoNoCheck得到了packageInfo对象,根据该对象得到ClassLoader后通过反射构建了service,获取了service对象之后,利用ContextImpl的静态方法得到了context,context设置了service为其外部代言人,之后创建了application,将service attach上去,最后启动onCreate方法。

       有一个疑问,为什么在创建了service的过程中需要构建application对象呢?可能是跟后台service依旧属于一个application,虽然没有前台界面的展示,没有明显的application构建,但是service需要依赖application,所以针对无界面、后台service启动的情况下需要创建application为service提供attach支持。

       整体流程如下:

三、总结

      通过第二部分的分析,可以总结如下:

      不论是启动app还是某个activity,亦或是service,最初都需要提交给AmS,AmS相当于总负责人,总负责人处理好消息后打包成数据(ApplicationInfo、ActivityInfo及ServiceInfo),并将数据通过跨进程传递的方式递交给ActivityThread进行处理,ActivityThread在接收时对外首先暴露ApplicationThread这个Binder接口,然后获取到相应的数据之后执行创建application、activity及service的流程;同时在创建这仨时,通过ContextImpl的静态方法来创建ContextImpl对象,对象创建完成之后通过setOuterContext方法指定各类的外部代言人,但是该方法的参数是Context,而恰好Application、Activity及Service都继承自Context,所有可以有效充当contextImpl的外部代言人,来调用contextImpl中的方法,这就是application、activity及service是context的本质,本质在于以代言人的身份调用方法(好像属于一类设计模式?)。

     多提一句,contextImpl内的核心实现其实大部分都在LoadApk方法内具体完成的。

推荐阅读更多精彩内容