Android插件化之启动Activity

在上一篇文章Android插件化之ClassLoader中,咱们已经能够成功加载apk,可是尚未办法启动插件中的Activity。咱们知道,若是要启动一个Activity,那么这个Activity必须在AndroidManifest.xml中注册。所以,若是咱们要启动插件中的Activity,那么这个Activity事先必须在宿主的AndroidManifest.xml中预注册。这样一来就会有两个问题:git

  1. 插件中的Activity在宿主的AndroidManifest.xml中注册,开发不太友好。
  2. 只能启动插件中事先定义好的Activity,没有办法动态增长Activity。

所以,现有的插件化框架都会有一套越过AndroidManifest.xml注册而启动Activity的机制。本文的目的就是分析这套机制。github

Activity的启动流程

不考虑多进程的状况,Activity的启动是Binder双向通讯的一个过程,它由咱们的app进程和AMS所在的system_server进程共同完成。system_server是一个系统级的进程,其内部的AMS负责管理全部app的Activity状态。所以,Android是不容许咱们对AMS进行修改的。那么,插件化技术只能在咱们app进程里作文章了。api

下图我根据6.0的源码画出了Activity的启动流程图,我省略了咱们不须要关心的AMS部分。 app

正如上图所示,我将Activity的启动流程分为了两部分。上部分描述了app从发起启动activity请求到AMS接受到该请求的过程。下部分描述了AMS处理完成后响应app进程启动Activity的过程。框架

请求阶段

上图不管是Activity的statActivity()仍是startActivityForResult()最终都调用了Instrumentation.execStartActivity()。咱们继续定位到Instrumentation.execStartActivity()。post

execStartActivity()很是简单。它将传进来的参数进一步包装后传给ActivityManagerNative.getDefault()的startActivity()。ActivityManagerNative.getDefault()返回的实际上是一个ActivityManagerProxy对象。ActivityManagerProxy是ActivityManagerService在app进程中的Binder代理对象。调用ActivityManagerProxy.startService()最后会调用ActivityManagerService.startService()。这样请求就到了ActivityManagerService。ActivityManagerNative.getDefault()以下:学习

到此,请求启动Activity的过程就分析完了。插件

响应阶段

前面咱们提到过,在不考虑多进程的状况下,Activity的启动过程是一个Binder双向通讯的过程。AMS要主动与app进程通讯要依靠请求启动Activity阶段传过来的IBinder对象,这个IBinder对象就是上面介绍过的Instrumentation.execStartActivity()中的 whoThread对象,它其实是一个ApplicationThreadProxy对象,用来和ApplicationThread通讯。AMS通知app进程启动Activity是经过调用ApplicationThreadProxy.scheduleLaunchActivity()完成的。根据Binder通讯,ApplicationThread.scheduleLaunchActivity()会被调用。咱们就从ApplicationThread.scheduleLaunchActivity()开始分析。3d

scheduleLaunchActivity()将从AMS中传过来的参数封装成ActivityClientRecord对象,而后将消息发送给mH,mH是一个Handler对象。 代理

H是ActivityThread的内部类,继承自Handler,它在收到LAUNCH_ACTIVITY的消息后,会调用ActivityThread.handlerLaunchActivity()。

handleLaunchActivity()主要调用了两个方法:performLaunchActivity()和handleResumeActivity()。performLaunchActivity()会完成Activity的建立,以及调用Activity的onCreate()、onStart()等方法。handleResumeActivity()会完成Activity.onResume()的调用。咱们继续跟踪performLaunchActivity()。

上述代码在关键位置都加了注释。经过注释咱们明白了Activity的建立以及onCreate()的调用都是在Instrumentation中完成的。这里须要注意一下Activtiy.attach()的调用时机,咱们会在下文中用到。Instrumentation具体的代码以下:

到此AMS通知app进程启动Activity的流程就结束了。

偷天换日,Hook Instrumentation越过AndroidManifest检测

以上内容算是内功部分,接下来就到了学习具体招式的时候了。

要启动没有在AndroidManifest.xml中注册的Activity,其核心是就是偷天换日。怎么作呢?经过一个例子说明。

假如在插件中有一个未在AndroidManifest.xml注册的TargetActivity,咱们想启动它,能够分为三步。

  1. 在AndroidManifest.xml中预先注册一个咱们项目中没有的Activity,例如ProxyActivity。咱们把这种行为称为插桩。
  2. 在请求启动Activity阶段,咱们把TargetActivity替换成AndroidManifest中预先注册的ProxyActivity。
  3. 在AMS响应阶段,Activity实例产生以前,咱们再作一个彻底相反的动做。即把响应信息中要启动的ProxyActivity替换回TargetActivity。

第一步十分简单,没什么好说的。要实现第二步和第三步就须要用到Activity启动流程的知识了。

在Activity启动流程中,Instrumentation不管在请求阶段仍是响应阶段都扮演着重要的角色。在请求阶段Instrumentation.execStartActivity()会被调用,而在响应阶段Instrumentation.newActivity()会被调用。所以若是咱们能够Hook Instrumentation,那么咱们就能够在execStartActivity()和newActivity()分别完成第二步和第三步中的功能。

再谈Instrumentation

ActivityThread中的Instrumentation

咱们知道,每个Java程序都有一个main()方法。Android App的main()方法就在ActivityThread中。

在main()方法中,ActivityThread会被初始化并最终把对象保存在静态的sCurrentActivityThread中。在一个app进程中只有一个ActivityThread实例sCurrentActivityThread。sCurrentActivityThread能够经过ActivityThread.currentActivityThread()拿到。

attach()中,mgr.attachApplication(mAppThread)这段代码又是一个Binder双向通讯的过程,它主要为建立Application对象服务。整个通讯过程和Activity启动过程相似,我就再也不详细介绍了。在通讯的最后,ActivtiyThread.handleBindApplication()被调用,而在方法内部,Instrumentation被初始化。

总结一下,一个App进程,只有一个ActivityThread对象,这个对象保存在sCurrentActivityThread中,能够经过ActivityThread.currentActivityThread()获取。ActivityThread的mInstrumentation会在Application建立以前初始化。

Activity中的Instrumentation

Activtiy中的Instrumentation是经过Activity.attach()传进来的。

Activity.attach()在介绍Activity启动流程时提到过。它会在ActivityThread.performLaunchActivity()中被调用。

这样ActivtyThread把本身内部的Instrumentation传递到了Activity中。

Hook Instrumentation

经过以上分析,咱们知道,要Hook app的Instrumentation,只须要替换掉ActivityThread的Instrumentation便可。可是,Android SDK没有为咱们提供任何关于ActivityThread的api。要访问Android SDK中不存在的类或方法,咱们学习一下VirtualAPK是怎么作的。

在VirtualAPK中有个叫AndroidStub的module。它的结构以下:

VirtualAPK又从新声明了这些Android SDK没有提供的Framework层的类。这些类只有方法的声明,如ActivityThread中的内容以下:

这样咱们就可使用这些Android SDK没有提供的类或隐藏的方法了。须要注意的一点是,AndroidStub应该只参与编译过程,这很简单,用compileOnly依赖就能够了。

接下来,咱们就能够经过反射替换ActivitThread的Instrumentation了。代码以下:

上面的VAInstrumentation是对系统Instrumentation的代理类。在VAInstrumentation的内部咱们能够加入任何咱们想要的逻辑。

在Instrumentation.execStartActivity()执行前将咱们要启动的Activity替换成预注册的ProxyActivity。

在Instrumentation.newActivity()执行前将预注册的ProxyActivity替换回咱们要启动的Activity。

结尾

Android插件化开篇中我说过,每一篇文章的最后都会是一个Demo,这些Demo串联起来就是一个插件化框架,因此我在Github上建了一个项目VirtualApkLike,Demo都会以不一样的分支放到这里。本文的Demo在startActtivity分支上。

相关文章
相关标签/搜索