通过对嵌入式企鹅圈原创团队成员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、自动驾驶等技术。欢迎扫码关注微信公众号:嵌入式企鹅圈,实时推送原创文章!

Android Small插件化框架解读——Activity注册和生命周期的更多相关文章

  1. Android Small插件化框架源码分析

    Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github ...

  2. 自己动手写Android插件化框架,让老板对你刮目相看

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由达文西发表于云+社区专栏 最近在工作中接触到了Android插件内的开发,发现自己这种技术还缺乏最基本的了解,以至于在一些基本问题上浪 ...

  3. [置顶] 滴滴插件化框架VirtualAPK原理解析(一)之插件Activity管理

    上周末,滴滴与360都开源了各自的插件化框架,VirtualAPK与RePlugin,作为一个插件化方面的狂热研究者,在周末就迫不及待的下载了Virtualapk框架来进行研究,本篇博客带来的是Vir ...

  4. Android插件化框架

    1.   dynamic-load-apk/DL动态加载框架 是基于代理的方式实现插件框架,对 App 的表层做了处理,通过在 Manifest 中注册代理组件,当启动插件组件时,首先启动一个代理组件 ...

  5. Android 全面插件化 RePlugin 流程与源码解析

    转自 Android 全面插件化 RePlugin 流程与源码解析 RePlugin,360开源的全面插件化框架,按照官网说的,其目的是“尽可能多的让模块变成插件”,并在很稳定的前提下,尽可能像开发普 ...

  6. MVC 插件化框架支持原生MVC的Area和路由特性

    .NET MVC 插件化框架支持原生MVC的Area和路由特性 前面开放的源码只是简单的Plugin的实现,支持了插件的热插拔,最近晚上偶然想到,原生的MVC提供Areas和RouteAtrribut ...

  7. 开源的JavaScript插件化框架MinimaJS

    本文介绍我开发的一个JavaScript编写的插件化框架——MinimaJS,完全开源,源码下载地址:https://github.com/lorry2018/minimajs.该框架参考OSGi规范 ...

  8. Activity完整的生命周期

    首语:群里看到一位网友说:你能说出Activity的完整生命周期吗?看到这句话,我也在反思自己,我也是个fresh,所以想找个时间仔细的扒一扒Activity生命周期. 首先拿一张简单而又复杂的生命周 ...

  9. Android系列之Fragment(二)----Fragment的生命周期和返回栈

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

随机推荐

  1. Python模块之"prettytable"

    Python模块之"prettytable" 摘要: Python通过prettytable模块可以将输出内容如表格方式整齐的输出.(对于用Python操作数据库会经常用到) 1. ...

  2. ssh reverse tunnel

    ssh反向通道的可用场景之一:从外网访问内网的主机.所必须的是你需要一个有ssh登录权限的公网主机. 步骤如下(将内网主机称作A,公网ssh主机地址为hostP ): 1.在内网A上执行 :local ...

  3. Redis各类型应用场景

    Redis的六种特性 l ,重要消息的,然后工作线程可以选择按 ret = r.zincrby("login:login_times", 1, uid) //那么如何获得登录次数最 ...

  4. 邻接矩阵有向图(二)之 C++详解

    本章是通过C++实现邻接矩阵有向图. 目录 1. 邻接矩阵有向图的介绍 2. 邻接矩阵有向图的代码说明 3. 邻接矩阵有向图的完整源码 转载请注明出处:http://www.cnblogs.com/s ...

  5. thinkphp 前台html调用函数 格式化输出

    仅仅是输出变量并不能满足模板输出的需要,内置模板引擎支持对模板变量使用调节器和格式化功能,其实也就是提供函数支持,并支持多个函数同时使用.用于模板标签的函数可以是PHP内置函数或者是用户自定义函数,和 ...

  6. (实用篇)PHP实现队列及队列原理

    队列是一种线性表,按照先进先出的原则进行的: PHP实现队列:第一个元素作为队头,最后一个元素作为队尾 <?php /** * 队列就是这么简单 * * @link */ $array = ar ...

  7. Android开发随笔之ScrollView嵌套GridView[ 转]

    今天在开发中用到了需要ScrollView嵌套GridView的情况,由于这两款控件都自带滚动条,当他们碰到一起的时候便会出问题,即GridView会显示不全,为了解决这个问题查了N多资料,某个谷歌的 ...

  8. 五指cms筛选功能的实现:

    筛选功能的实现: $_POST['page_urlrule'] = 'tuan-{$pinpai}-{$renqun}-{$type}-{$price}-{$area}-{$tese}-{$st}-{ ...

  9. Xamarin开发教程如何使用Xamarin开发Android应用

    Xamarin开发教程如何使用Xamarin开发Android应用 如何使用Xamarin开发Android应用 在了解了Xamarin和Andriod系统之后,下面我们需要了解一下如何使用这些工具和 ...

  10. Android之来历

    Android一词的本义指“机器人”,同时也是谷歌于2007年11月5日宣布的基于Linux平台的开源手机操作系统的名称,该平台由操作系统.中间件.用户界面和应用 软件组成,号称是首个为移动终端打造的 ...