Android Small插件化框架解读——Activity注册和生命周期
通过对嵌入式企鹅圈原创团队成员degao之前发表的《Android Small插件化框架源码分析》的学习,对Android使用的插件化技术有了初步的了解,但还是有很多需要认真学习的地方,特别是大部分知识都需要结合虚拟机和Androidframwork的原理才能慢慢理解。比如,文中作者提到了插件化框架要解决的三个核心问题:
1)插件类的加载;
2)插件资源的处理;
3)Activity注册和生命周期问题;
其中第3点作者是这样解释的,“大部分插件化框架解决办法都是采用在宿主工程里预先注册Activity占坑,然后通过占坑Activity将生命周期回调传回给插件Activity的方式。这里Small处理的比较有特色,通过替换 ActivityThread 里的mInstrumentation,在Instrumentation的newActivty实现里面实例化了插件Activity,通过较小改动就能完全解决生命周期回调的问题。”。
这里面提到的ActivityThread、Instrumentation、newActivity是什么东西呢?该如何理解“预先注册Activity占坑”、“替换 ActivityThread 里的mInstrumentation”这些步骤呢?带着这些问题,我学习了Android framework中启动Activity的源代码,基本上搞懂了Small插件化框架是如何处理和控制Activity生命周期的了,在这里总结一下这个学习过程。
我们App中Activity的生命周期由ActivityManagerService来管理,它决定着什么时候该调用onCreate、onResume这些生命周期的回调函数,把Activity放在哪个栈里,上下文之间的关系是怎样的等等。当我们把一个插件apk用classLoader加载进宿主应用后,插件apk里的Activity并不能被ActivityManagerService所管理,因为它只是作为一般的class被加载到宿主的进程空间中,而没有在AndroidManifest.xml中声明,因此通过startActivity启动插件里的activity时会抛出异常:
“android.content.ActivityNotFoundException: Unable to find explicit activity class… … … …
have you declared this activity in your AndroidManifest.xml?”
显然,宿主应用自己来管理插件中activity的生命周期有很大难度,最好还是把这项工作交给ActivityManagerService。可以预先在AndroidManifest.xml中定义一些空的Activity,给它们预先起上特定的名字(ProxyActivty$1),当要启动插件里的activity(PluginActivity1)时,Small框架就先启动ProxyActivty$1,这样它的生命周期就会交由AMS去管理了,在真正构造和加载Activity的地方,再用PluginActivity1这个类的实例去替换ProxyActivty$1的实例。这样一来,AMS中管理的是ProxyActivty$1这个activity,但实际上在宿主应用进程中实例化的却是插件里的PluginActivity1,它里面的onCreate、onResume等函数会被依次调用,生命周期等问题就交由AMS去控制了,相当于欺骗了AMS和系统。
这就是解决Activity注册和生命周期问题的方法。要理解这个过程,需要分三步走:
1)App所在的客户端进程和AMS所在的system_server进程之间是如何交互的;
2)搞清启动一个Activity的前前后后,特别是从客户端进程进入到AMS的入口,和从AMS返回到客户端进程的入口,从中找到activity是怎样被交给AMS的,activity是在哪儿实例化的;
3)要关注上面这个过程中具体涉及到的类和方法,从中选择合适的Hook点,用Java反射的方法来进行动态替换,自己实现偷梁换柱的动作。
一、App和AMS通过Binder交互
1. AMS的初始化
ActivityManagerService运行在system_server进程中,在SystemServer启动后完成初始化工作,然后加入到ServiceManager中。ServiceManager管理所有的Android系统服务,客户端应用如果要使用系统服务,调用getSystemService接口,ServiceManager就会返回给客户端应用对应的系统服务的IBinder对象。
在SystemServer的run函数中会调用startBootstrapServices先启动AMS、PMS这样关键的系统服务,它里面会初始化AMS。先调SystemServiceManager的startService方法,注意传入的参数不是ActivityManagerService.class,而是它的一个静态内部类Liftcycle。
startService会调用同名的函数,用反射构造出传入class的实例,也就是ActivityManagerService.Liftcycle的实例并返回。
然后再调Lifecycle的getService函数得到ActivityManagerService的实例,把它赋给SystemServer中的mActivityManagerService。
在startBootstrapServices最后会调setSystemProcess把AMS加到ServiceManager中。
2. AMS在服务端的代理ActivityManagerNative
Android Binder机制在客户端和服务端各有一个代理Proxy和Stub,它们之间通过transact和onTransact进行进程间通信,AMS在服务端的代理是ActivityManagerNative,它和客户端的代理ActivityManagerProxy进行Binder通信,调用AMS提供的各种功能。AMS对外提供的接口是IActivityManager,它里面包含了AMS对外提供服务的全部函数,有startActivity、startService、registerReceiver等等,ActivityManagerNative实现了IActivityManager,但它只是一个抽象类,只用于和客户端的Binder通信,startActivity等的具体实现在它的子类ActivityManagerService中完成。
3.AMS在客户端的代理ActivityManagerProxy
应用App使用AMS提供的功能,比如startActivity,是通过AMS在客户端的代理ActivityManagerProxy发起的。这里ActivityManagerNative的getDefault方法返回就是ActivityManagerProxy的实例。
getDefault返回的是ActivityManagerNative中的一个单例gDefault,它通过ServiceManager获取到AMS的IBinder,然后再通过asInterface得到ActivityManagerProxy。
ActivityManagerNative的asInterface方法在客户端和服务端都会被调用。在客户端,因为IBinder在native的实现BpInterface没有重载queryLocalInterface,所以返回它的缺省实现,缺省为null,走new ActivityManagerProxy的分支。可见,ActivityManagerProxy的asBinder返回的就是从ServiceManager中取到的AMS的IBinder对象。
4. ActivityThread在客户端的代理ApplicationThreadNative
与服务端相似,App在客户端进程中完成实例化Activity、调用onCreate等生命周期函数的功能,也不能被AMS直接调用,而是通过自己在客户端的代理ApplicationThreadNative来处理。虽然代理的名字有Thread字样,但它并不表示App所在的进程,ActivityThread才是描述客户端进程的类。也就是说当新创建一个应用进程时,系统就会为我们新构造一个ActivityThread对象。IApplicationThread是个接口,里面有scheduleLaunchActivity、scheduleCreateService等需要在客户端进程中调用的方法。ApplicationThreadNative是个Binder,负责与它在服务端的代理ApplicationThreadProxy通信,同时也实现了IApplicationThread接口,但具体的实现放在了它的子类ApplicationThread中。
ApplicationThread对象被客户端应用进程ActivityThread所持有,ActivityThread与AMS的交互实际上遵循了设计模式中的代理模式:
üIApplicationThread是抽象对象角色,提供了要使用的业务逻辑的抽象接口。
üActivityThread是目标对象角色,AMS不能直接与它交互、直接使用它的功能,都是通过它的代理类来进行。
üApplicationThread是代理对象角色,是ActivityThread的代理类,实现了具体的业务逻辑。与一般的代理模式不同,它不是直接持有ActivityThead的一个引用,而是把处理的请求发到ActivityThread内部的一个Handler上。
5.ActivityThread在服务端的代理ApplicationThreadProxy
与获取ActivityManagerNative在客户端的代理ActivityManagerProxy的过程类似,ApplicationThreadNative在服务端,也就是AMS所在的system_server进程中的代理,也是通过调用一个叫asInterface的函数来获得的。
可见返回的ApplicationThreadProxy对象是IApplicationThread这个接口的类型,在ActivityManagerNative.java的onTransact中会用到这个客户端的代理,比如在App中调用startActivity时,ActivityManagerNative在服务端的onTransact函数里会调用AMS的startActivity,这时就通过ApplicationThreadNative.asInterface得到ApplicationThreadProxy,把它作为startActivity的参数传给AMS。ApplicationThreadProxy在这儿的用处是作为app的代理,代表这客户端的ActivityThread,因为AMS要处理多个客户端进程的请求,所以通过这个代理可以得到客户端的pid、uid、ProcessRecord等信息,还会通过它发起对客户端的Binder调用。
6.小结
Android的命名可以帮助我们区分这些类。ActivityThread代表了应用所在的进程,xxxNative是在本进程内的Binder代理类,xxxProxy是在对方进程的Binder代理类,熟悉Binder机制的话可以想到Binder在C++层的实现也是这样命名BnXXX和BpXXX的。
二、startActivity的过程
1.从App进入AMS的过程
调用startActivity有两种情况,一是Context(实际上是它的具体实现ContextImpl)调startActivity,一是Activity调startActivity。最终它们都会调用Instrumentation的execStartActivity。图15是ContextImpl调用的代码,mMainThread就是ActivityThread,Instrumentation是它里面的一个成员。图16是Activity调用的代码,startActivity先调用了startActivityForResult,在startActivityForResult中再调用了Instrumentation的execStartActivity。注意,在Context中调用startActivity时,因为上下文中没有Activity的栈,所以要加上FLAG_ACTIVITY_NEW_TASK这个flag。Instrumentation是工具、插桩的意思,顾名思义Activity只是在执行时经过Instrumentation,以便它起到监控和测试的功能。
execStartActivity中通过调用ActivityManagerNative.getDefault()获得AMS在客户端的代理对象ActivityManagerProxy,它是ActivityManagerNative类中的一个静态单例对象。然后调用它的startActivity方法,实际上是通过binder的transact发出命令,由AMS客户端的代理ActivityManagerNative的onTransact来处理,
App请求AMS的其他函数调用过程与此相似,无一例外的都是经过ActivityManagerNative里面的单例对象gDefault,它是从客户端进程发起调用进入AMS的中转站,这为我们提供了一个hook点,按照一开始所设想的那样,原来startActivity启动的是插件中的PluginActivity1,在这里可以hook gDefault,这样所有客户端向AMS发起的调用,比如startService之类的都可以被我们截获,然后就可以修改其中的参数,替换成含有AndroidManifest.xml中预先占坑的ProxyActivity$1的Intent了。对宿主应用来说,它启动的是PluginActivity1,对AMS来说,它管理的activity是ProxyActivity$1。
2.在AMS中处理的过程
AMS中处理startActivity的过程比较复杂,主要涉及了ActivityManagerService、ActivityStackSupervisor、ActivityStack、PackageManagerService、WindowManagerService等几个类。
在ActivityManagerService中,startActivity先是调用了startActivityAsUser,然后在startActivityAsUser中调用了ActivityStackSupervisor的startActivityMayWait。注意,第一个参数caller就是我们前面说过的app在服务端的代理ApplicationThreadProxy,它是一个Binder对象,实现了IApplicationThread。转到ActivityStackSupervisor中后,又在它和ActivityStack之间来回调用了许多次,主要是进行栈和Task的管理、解析Activity的信息,准备窗口的切换之类的工作,最后回到了ActivityStackSupervisor中,调用realStartActivityLocked函数。
在realStartActivityLocked函数中,app是ProcessRecord类型,app.thread是IApplicationThread类型,也就是从客户端的代理ApplicationThreadProxy,在这儿调用了它的scheduleLaunchActivity方法,接下来就会回到app的进程空间里。
我们无法在AMS中去hook类和方法,因为它在system_server进程中,我们没办法跨越进程的鸿沟,当然也没有权限注入到system_server进程中。而且,AMS中的处理流程非常复杂,都是栈的管理之类的逻辑,很难找到hook点,来把占坑的Activity替换回插件里的activity,所以我们还是得回到app进程中。
3.重新回到app进程
前面说过在ApplicationThreadNative中会处理AMS对ActivityThread的binder调用,它的onTransact函数会调用scheduleLaunchActivity,其具体实现在ApplicationThread中。ApplicationThread是ActivityThread里的一个内部类,它的scheduleLaunchActivity的实现就是发一个LAUNCH_ACTIVITY类型的message到ActivityThread中的一个handler上。
这个名为H的handler中用handleLaunchActivity函数来处理AMS发起的scheduleLaunchActivity调用。handleLaunchActivity里又调用了performLaunchActivity。
performLaunchActivity中又用到了Instrumentation类,调它的newActivity函数构造出activity对象。newActivity函数很简单,直接用classLoader加载了Activity类,然后用反射调它的构造函数newInstance出activity实例。
然后再回到performLaunchActivity中,在通过netActivity得到activity的实例后,接下来就该调用它的生命周期函数onCreate了,照旧还是通过Instrumentation来完成,调用它的callActivityOnCreate函数。
在callActivityOnCreate里会调用Activity的performCreate函数,它里面调用的就是我们熟悉的onCreate了。
到此为止,我们简单地跟踪了startActivity的整个流程,根据前面的设想,这里的Activity还是占坑的ProxyActivity$1,要是能把它替换回插件的PluginActivity1就好了。从前面的流程可以看出,Instrumentation这个类是startActivity整个过程中的必经之路,无论是从app到AMS,还是从AMS回到app都会经过它,要是能hook它就好了,因为它是ActivityThread里的一个成员mInstrumentation,所以我们在客户端进程中可以通过反射拿到ActivityThread对象,也可以拿到mInstrumentation。
三、利用反射完成动态代理
前面我们梳理了startActivity的整个调用流程,发现在app和AMS之间来回调用的过程中有两个可以hook的点,下面我们来具体分析一下:
1)从app进程进入到AMS中,都是通过AMS在客户端进程的代理ActivityManagerNative类里的静态单例对象gDefault来发起binder调用的,它是ActivityManagerProxy类型。我们可以在这个地方换掉startActivity传入的参数,把占坑的ProxyActivity$1传给AMS。但是从AMS返回到app里,调用scheduleLaunchActivity等一系列动作却不会经过它,而是经过客户端的代理ApplicationThreadNative发起对客户端的binder调用,但如果hook ApplicationThreadNative也不能解决问题,因为最后真正构造出activity实例的步骤是在Instrumentation的newActivity函数中,ApplicationThreadNative对象离着它还很远。
2)参考第一部分我们分析的流程,从app进入AMS和从AMS返回到app,在客户端进程中都要经过Instrumentation这个类。从app进入AMS时,调用它的execStartActivity函数,在execStartActivity中再调ActivityManagerNative.getDefault().startActivity,发起binder调用。从AMS返回app时,调用它的newActivity,用发射构造出activity的实例,所以Instrumentation才是我们需要hook的类,可以在进入AMS时把插件的PluginActivity1换成占坑的ProxyActivity$1,在回到app构造真正的activity时,new出插件的PluginActivity1。这样一来,对AMS来说它管理的是ProxyActivity$1,对宿主应用来说,插件PluginActivity1在它的进程里已经被实例化和运行了
现在我们来看Small框架中的代码是处理的。
ActivityThread中有一个单例对象sCurrentActivityThread,它是在attche时被赋值的。前面说过Instrumentation在ActivityThread中的实例是其成员mInstrumentation,所以我们先通过ActivityThread的静态方法currentActivityThread得到它的实例sCurrentActivityThread,然后找到它的成员mInstrumentation,把它的引用对象换成自己实现的InstrumentationWrapper。InstrumentationWrapper也是Instrumentation类型,它里面实现了execStartActivity和newActivity这两个方法,用来替换掉原来的函数调用。
其中,execStartActivity函数在不同android版本中的参数有所不同,wrapIntent函数完成了替换Intent的动作,把PluginActivity1的Intent替换成了ProxyActivity$1的Intent。最后在ReflectAccelerator中完成原来函数的调用。
newActivity中的unwrapIntent是wrapIntent的反向工作,得到的className是插件中的PluginActivity1的类名,最后再调用原来的newActivity完成activity的构造。
这样最终完成了用PluginActivity1替换AndroidManifest.xml中的ProxyActivity$1的偷天换日的工作,插件里的PluginActivity1既可以被正常的加载运行也不用担心它的生命周期之类的管理了。
- 嵌入式企鹅圈原创团队由阿里、魅族、nvidia、龙芯、炬力、拓尔思等资深工程师组成。百分百原创,每周两篇,分享嵌入式、Linux、物联网、GPU、Android、自动驾驶等技术。欢迎扫码关注微信公众号:嵌入式企鹅圈,实时推送原创文章!