转自:http://m.blog.youkuaiyun.com/article/details?id=52943370
参考:
http://blog.youkuaiyun.com/zhaokaiqiang1992/article/details/49428287
http://www.w2bc.com/article/126583
Android Small框架是Android中一个非常优秀的插件化框架,有时我们不仅要学会使用它,也要去理解它的原理和实现逻辑。这片博客主要解析Android Framework中StartActivity()的启动原理,同时对Small框架中实现启动插件Activity的源码进行解析(App to AMS),希望对那些想研究Small源码的同学有帮助。
Activity启动过程解析
1、zygote进程
zygote意为“受精卵“。Android是基于Linux系统的,而在Linux中,所有的进程都是由init进程直接或者是间接fork出来的,zygote进程也不例外。
每一个App其实都是
- 一个单独的dalvik虚拟机
- 一个单独的进程
所以当系统里面的第一个zygote进程运行之后,在这之后再开启App,就相当于开启一个新的进程。而为了实现资源共用和更快的启动速度,Android系统开启新进程的方式,是通过fork第一个zygote进程实现的。所以说,除了第一个zygote进程,其他应用所在的进程都是zygote的子进程,这下你明白为什么这个进程叫“受精卵”了吧?因为就像是一个受精卵一样,它能快速的分裂,并且产生遗传物质一样的细胞!
2、SystemServer
SystemServer也是一个进程,而且是由zygote进程fork出来的。
这个进程是Android Framework里面两大非常重要的进程之一——另外一个进程就是上面的zygote进程。
为什么说SystemServer非常重要呢?因为系统里面重要的服务都是在这个进程里面开启的,比如
ActivityManagerService、PackageManagerService、WindowManagerService等等。
3、ServiceManager
ServiceManager管理所有的Android系统服务,客户端应用如果要使用系统服务,调用getSystemService接口,ServiceManager就会返回给客户端应用对应的系统服务的IBinder对象。
3、Android的服务端和客户端
其实服务器客户端的概念不仅仅存在于Web开发中,在Android的框架设计中,使用的也是这一种模式。
- 服务器端(server)指的就是所有App共用的系统服务,比如我们这里提到的ActivityManagerService,PackageManagerService、WindowManagerService等等,这些基础的系统服务是被所有的App公用的,当某个App想实现某个操作的时候,要告诉这些系统服务。
- 客户端(client)指的就是各个独立的App进程。
4、开启APP的过程
打开一个App,那么我们知道了包名和MainActivity类名之后就可以打开。但是,我们的App通过调用startActivity()并不能直接打开另外一个App,startActivity()方法会通过一系列的调用,最后还是告诉AMS说:“我要打开这个App,我知道他的住址和名字,你帮我打开吧!”所以是AMS来通知zygote进程来fork一个新进程,来开启我们的目标App的。这就像是浏览器想要打开一个超链接一样,浏览器把网页地址发送给服务器,然后还是服务器把需要的资源文件发送给客户端的。
App和AMS(SystemServer进程)还有zygote进程分属于三个独立的进程,他们之间如何通信呢?
App与AMS通过Binder进行IPC(Inter-process communication)通信,AMS(SystemServer进程)与zygote通过Socket进行IPC通信。
5、AMS
AMS是什么:
AMS(ActivityManagerService)是一个系统服务,App中Activity的生命周期由AMS管理,它决定着什么时候该调用onCreate、onResume这些生命周期的回调函数,把Activity放在哪个栈里,上下文之间的关系是怎样的等等。
在Android系统中,任何一个Activity的启动都是由AMS和应用程序进程(主要是ActivityThread)相互配合来完成的。AMS服务统一调度系统中所有进程的Activity启动,而每个Activity的启动过程则由其所属的进程具体来完成。
AMS对于FrameWork层的重要性不言而喻,Android的四大组件无一不与它打交道:
-
startActivity 最终调用了AMS的 startActivity 系列方法,实现了Activity的启动;Activity的生命周期回调,也在AMS中完成;
-
startService,bindService 最终调用到AMS的startService和bindService方法;
- 动态广播的注册和接收在 AMS 中完成(静态广播在 PMS 中完成)
- getContentResolver 最终从 AMS 的 getContentProvider 获取到ContentProvider
AMS的初始化:
2、ActivityThread
ActivityThread是描述客户端进程的类。也就是说当新创建一个应用进程时,系统就会为我们新构造一个ActivityThread对象。
ActivitiyThread建立了应用进程运行的框架,并提供了一个IActivityThread接口作为与AMS的通讯接口.通过该接口AMS可以将Activity的状态变化传递到客户端的Activity对象
ActivityThread主要用来启动应用程序的主线程,并且管理在应用端跟用户打交道的activity。在应用端的activity信息全部被存储在ActivityThread的成员变量mActivities中。
6、Android Binder机制
什么是Binder机制
Android Binder机制在客户端和服务端各有一个代理Proxy和代理Native,它们之间通过transact和onTransact进行进程间通信
-
AMS在服务端的代理是ActivityManagerNative,它和客户端的代理ActivityManagerProxy进行Binder通信,调用AMS提供的各种功能。
-
ActivityThread在服务端的代理ApplicationThreadProxy,它和客户端的代理ApplicationThreadNative进行Binder通信,根据AMS的要求负责调度和执行activities、broadcasts和其它操作。
Android的命名可以帮助我们区分这些类。ActivityThread代表了应用所在的进程,xxxNative是在本进程内的Binder代理类,xxxProxy是在对方进程的Binder代理类
AMS中ActivityManagerNative和ActivityManagerProxy
IActivityManager作为ActivityManagerProxy和ActivityManagerNative的公共接口,
所以两个类具有部分相同的接口,可以实现合理的代理模式;
ActivityManagerProxy类在ActivityManagerNative类中被调用;
ActivityManagerNative是个抽象类,真正发挥作用的是它的子类ActivityManagerService(系统Service组件)。
App使用AMS提供的功能,比如startActivity,是通过AMS在客户端的代理ActivityManagerProxy发起的。这里ActivityManagerNative的getDefault方法返回就是ActivityManagerProxy的实例(后面详细介绍)。
ActivityThread中ApplicationThreadNative和ApplicationThreadProxy
与服务端相似,App在客户端进程中完成实例化Activity、调用onCreate等生命周期函数的功能,也不能被AMS直接调用,而是通过自己在客户端的代理ApplicationThreadNative来处理。
ApplicationThread对象被客户端应用进程ActivityThread所持有,ActivityThread与AMS的交互实际上遵循了设计模式中的代理模式:
- ApplicationThread是抽象对象角色,提供了要使用的业务逻辑的抽象接口。
- ActivityThread是目标对象角色,AMS不能直接与它交互、直接使用它的功能,都是通过它的代理类来进行。
- ApplicationThread是代理对象角色,是ActivityThread的代理类,实现了具体的业务逻辑。与一般的代理模式不同,它不是直接持有ActivityThead的一个引用,而是把处理的请求发到ActivityThread内部的一个Handler上。
7、Instrumentation
每个Activity都持有Instrumentation对象的一个引用,但是整个应用程序进程只会存在一个Instrumentation对象。
Instrumentation可以理解为应用进程的管家,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作。
Instrumentation重要性
Instrumentation类里面的方法大多数和Application和Activity有关,是的,这个类就是完成对Application和Activity初始化和生命周期的工具类。
例如挑一个callActivityOnCreate()看:
- 1
- 2
- 3
- 4
- 5
activity.performCreate(icicle);这一行里面就调用了传说中的Activity的入口函数onCreate()。
Activity.performCreate():
- 1
- 2
- 3
- 4
- 5
Activity生命周期方法的调用离不开Instrumentation,他可以说是一个大管家,但是,这个大管家比较害羞,是一个女的,管内不管外,是老板娘~
那么你可能要问了,老板是谁呀?
老板当然是大名鼎鼎的ActivityThread了!
我们前面说过,App和AMS是通过Binder传递信息的,那么ActivityThread就是专门与AMS的外交工作的。
AMS说:“ActivityThread,你给我暂停一个Activity!”
ActivityThread就说:“没问题!”然后转身和Instrumentation说:“老婆,AMS让暂停一个Activity,我这里忙着呢,你快去帮我把这事办了把~”
于是,Instrumentation就去把事儿搞定了。
所以说,AMS是董事会,负责指挥和调度的,ActivityThread是老板,虽然说家里的事自己说了算,但是需要听从AMS的指挥,而Instrumentation则是老板娘,负责家里的大事小事,但是一般不抛头露面,听ActivityThread的安排。
8、关于Hook
概念
我们知道代理有比原始对象更强大的能力,比如飞到国外买东西,比如坑钱坑货;那么很自然,如果我们自己创建代理对象,然后把原始对象替换为我们的代理对象,那么就可以在这个代理对象为所欲为了;修改参数,替换返回值,我们称之为Hook。
Hook寻找原则
什么样的对象比较好Hook呢?自然是 容易找到的对象 。什么样的对象容易找到? 静态变量和单例 ;在一个进程之内,静态变量和单例变量是相对不容易发生变化的,因此非常容易定位,而普通的对象则要么无法标志,要么容易改变。我们根据这个原则找到所谓的Hook点。
Hook过程简要总结
- 寻找Hook点,原则是静态变量或者单例对象,尽量Hook pulic的对象和方法,非public不保证每个版本都一样,需要适配。
- 选择合适的代理方式,如果是接口可以用动态代理;如果是类可以手动写代理也可以使用cglib。
- 偷梁换柱——用代理对象替换原始对象
9、startActivity的过程
1.从App进入AMS的过程
调用startActivity有两种情况:
- 一种是Context(实际上是它的具体实现ContextImpl)调startActivity
- 另一是Activity调startActivity
在ContextImpl.java中的startActivity代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
在Activity.java中的startActivity先调用了startActivityForResult:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
然后startActivityForResult中再调用了Instrumentation的execStartActivity:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
当startActivityForResult()调用之后,实际上还是调用了mInstrumentation.execStartActivity()
这两种过程最终它们都会调用Instrumentation的execStartActivity。
为什么要调用execStartActivity?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
因为execStartActivity中通过调用ActivityManagerNative.getDefault()获得AMS在客户端的代理对象ActivityManagerProxy,而startActivity,是通过AMS在客户端的代理ActivityManagerProxy发起的。
为什么ActivityManagerNative的getDefault方法返回就是ActivityManagerProxy的实例?
ActivityManagerNative中getDefault返回的是ActivityManagerNative中的一个单例gDefault:
- 1
- 2
- 3
gDefault通过ServiceManager获取到AMS的IBinder:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
然后再通过asInterface得到ActivityManagerProxy:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
startActivity的过程图解:
execStartActivity调用它的startActivity方法,在方法里面实际上是通过binder的transact发出命令,由AMS客户端的代理ActivityManagerNative的onTransact来处理,然后交由AMS来处理。
Binder代码的具体实现:
Binder本质上只是一种底层通信方式,和具体服务没有关系。为了提供具体服务,Server必须提供一套接口函数以便Client通过远程访问使用各种服务。这时通常采用Proxy设计模式:将接口函数定义在一个抽象类中,Server和Client都会以该抽象类为基类实现所有接口函数,所不同的是Server端是真正的功能实现,而Client端是对这些函数远程调用请求的包装。
为了更方便的说明客户端和服务器之间的Binder通信,下面以ActivityManagerServices和他在客户端的代理类ActivityManagerProxy为例。
ActivityManagerServices和ActivityManagerProxy都实现了同一个接口——IActivityManager。
虽然都实现了同一个接口,但是代理对象ActivityManagerProxy并不会对这些方法进行真正地实现,ActivityManagerProxy只是通过这种方式对方法的参数进行打包(因为都实现了相同接口,所以可以保证同一个方法有相同的参数,即对要传输给服务器的数据进行打包),真正实现的是ActivityManagerService。
但是这个地方并不是直接由客户端传递给服务器,而是通过Binder驱动进行中转。其实我对Binder驱动并不熟悉,我们就把他当做一个中转站就OK,客户端调用ActivityManagerProxy接口里面的方法,把数据传送给Binder驱动,然后Binder驱动就会把这些东西转发给服务器的ActivityManagerServices,由ActivityManagerServices去真正的实施具体的操作。
但是Binder只能传递数据,并不知道是要调用ActivityManagerServices的哪个方法,所以在数据中会添加方法的唯一标识码,比如前面的startActivity()方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
上面的START_ACTIVITY_TRANSACTION就是方法标识,data是要传输给Binder驱动的数据,reply则接受操作的返回值。
即
客户端:ActivityManagerProxy =====>Binder驱动=====> ActivityManagerService:服务器
而且由于继承了同样的公共接口类,ActivityManagerProxy提供了与ActivityManagerService一样的函数原型,使用户感觉不出Server是运行在本地还是远端,从而可以更加方便的调用这些重要的系统服务。
但是!这里Binder通信是单方向的,即从ActivityManagerProxy指向ActivityManagerService的,如果AMS想要通知ActivityThread做一些事情,应该咋办呢?
还是通过Binder通信,不过是换了另外一对,换成了ApplicationThread和ApplicationThreadProxy。
客户端:ApplicationThread <=====Binder驱动<===== ApplicationThreadProxy:服务器
他们也都实现了相同的接口IApplicationThread
剩下的和前面一样。
Small中启动插件Activity解析
1、Small框架中几个重要类的继承关系
2、调用ApkBundleLauncher.setUp()过程
ApkBundleLauncher.setUp():该方法主要为了注入instrumentation,为解决插件Activity的启动和生命周期问题做准备。
调用过程时序图:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
3、替换execStartActivity()过程
替换过程时序图:
4、加载插件Activity的关键函数源码分析
wrapIntent():
wrapIntent()方法是在自定义的InstrumentationWrapper类的execStartActivity函数中调用。从分析过程可以得知wrapIntent方法将插件activity类保存在intent的category中,同时将intent的component里面的类替换为宿主中声明的占位Activity,以通过ActivityManager的检查:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
dequeueStubActivity(ActivityInfo ai, String realActivityClazz):
dequeueStubActivity(ai, realClazz)该方法实现了为插件Activity找到一个对应启动模式的可用的存根Activity。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
Small框架中AndroidManifest中占坑Activity设置格式:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
为了方便大家理解该方法的逻辑关系我把每个变量对应的数值关系列出来,便于大家理解:
LAUNCH_MULTIPLE = 0
LAUNCH_SINGLE_TOP = 1
LAUNCH_SINGLE_TASK = 2
LAUNCH_SINGLE_INSTANCE = 3
STUB_ACTIVITY_TRANSLUCENT=PACKAGE_NAME+”.A”
STUB_ACTIVITY_PREFIX=PACKAGE_NAME+”.A1”
STUB_ACTIVITIES_COUNT = 4
countForMode = STUB_ACTIVITIES_COUNT=4
countForAll = 3countForMode = 3*4=12
mStubQueue = new String[countForAll]=new String[12]
launchMode={standard:0,singleTop:1,singleTask:2,singleInstance:3}
offset = (ai.launchMode - 1)countForMode=(ai.launchMode - 1) 4={0,4,8}
i = 0–3 availableId=0–3或-1 stubId=0–3或-1
availableId=-1:表示有存根Activity可用
availableId=其它:表示没有存根Activity可用
stubId=-1:表示该插件Activity是新的
stubId=其它:表示该插件Activity是开启的
当availableId=-1并且stubId=-1时:表示对应该启动类型的存根Activity已经满了,没有可以利用的了。
总结:至此我们了解了startActivity从app到AMS的过程,并且分析了Small对此过程怎么进行Hook的分析。下一张我们继续分析startActivity在AMS中处理,并且放回app的过程。