集成一个第三方相册功能,只需集成一个插件APK到项目中,无需集成额外代码,而且支持随时更新相册功能,无需发布版本更新,无需AndroidManifest中声明四大组件,这就是插件化。android
插件化可利用性很广,但事实上大多数开发者,由于未知而放弃使用,因此本篇将深刻浅出带你了解插件化原理,从基础到实现,插件化再也不是你陌生的领域。git
本篇主要涉及到:github
ps:若是你对此(1、二)已经十分了解,请自行略过。编辑器
Activity和Service的启动流程十分复杂,一个startActivity
的背后是无数的逻辑实现,这里不深刻讨论,但须要理解这个流程,由于插件化是在流程上动手脚,以达到绕过系统限制的目的。ide
下方图片是Activity启动的简化流程,能够看到,从Instrumentation
开始,到ActivityManagerService
和ActivityThread
结束,启动一个Activity,流程并不简单。工具
在Instrumentation
在execStartActivity
开始启动,到经过checkStartActivityResult
校验Activity是否在Manifest中声明,从图中能够看出,流程仍是至关繁琐的。
(Activity启动流程详见图片)性能
下方图片是Service启动的简化流程,一样能够看到,ActivityManagerService
和ActivityThread
一样起到了关键性的做用。插件化的关键,就在于Instrumentation
、ActivityManagerService
和ActivityThread
。优化
(Service启动流程详见图片)插件
为了更好理解插件化,如下图,是几个关键类的对应关系与实际做用,有点S/C的味道。它们的通讯是经过IBinder
,实现进程通讯的,能够看出,启动Activity和Service,ActivityThread
和ActivityManagerService
是关键,而且上面咱们知道,Instrumentation
是Activity的启动入口,因此实现插件化的流程,即可以在这些关键类上开刀。3d
(下图在插件化实现中起到关键做用)
好了,带了一波基础姿式的节奏,稍安勿躁,先这里在补充几个概念,若是你已经习得,能够跳过:
Hook:拦截某个内部流程,在其中作某些修改,以实现本身的逻辑。
Instrumentation:每一个Activity都有一个Instrumentation
对象,它是在Activity启动是被赋予的Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity();
这即是startActivityForResult的启动,同时返回启动结果。
占坑:声明一个不存在的Activity,如:<activity android:name=".A$1" android:launchMode="standard"/>
,这样启动.A$1这个Activity能够欺骗系统检测,而后再将插件Activity注入到.A$1这个坑位中。
插件化的实现就是在于加载、绕过系统限制、启动和管理插件等过程。按照VirtualApk的实现,大体流程为:
一、初始化Hook住Instrumentation
和ActivityThread
等。经过PackageParser(插件apk包信息)、AssetManager(资源文件Resources)、ClassLoader等加载一个Apk插件。
二、启动插件Activity:提早在主APP中占有坑位,经过替换Intent中的targetActivity,打开占坑声明的A$Activity,而后绕过AndroidManifest检测,再拦截newActivity方法中恢复targetActivity。
三、启动插件Service:经过启动一个代理Service统一管理,拦截全部Service方法,修改成startService到代理Service,在代理Service的onStartCommond
统一管理,建立/中止目标service 。
初始化过程当中,VirtualApk 建立了PluginManager ,而且hook住了Instrumentation
和SystemService,以下图所示。
如下图所示,VrutalApk经过Instrumentation
建立了一个VAInstrumentation
对象,VAInstrumentation
是一个继承Instrumentation
的类。
将VAInstrumentation
反射插入到ActivityThread
中,这样系统接下来关于Instrumentation
的操做,就会回到VAInstrumentation
中,被VrtualApk接管。
这里是如何拿到Instrumentation
的?
由于在ActivityThread
内部有一个sCurrentActivityThread
静态变量。以下图,经过反射sCurrentActivityThread
咱们能够获取当前ActivityThread
,而ActivityThread
的公开方法getInstrumentation
便可拿到Instrumentation对象。
另外,上方图1还有设置HandlerCallback
的流程,其实就是拦截了ActivityThread
中的mH
这个Handler的Callback,从【 1、 Activity/Service启动流程】流程图能够看到,mH的handleMessage处理不少Activity的启动状态。
以下图, 是Hook Service的流程,如图中注释所示,经过ActivityManagerNative
的getDefault
,拿到AndroidManagerService
(详见启动流程图),而VirtualApk经过自定义ActivityManagerProxy
,从新生成了一个IActivityManager
,而后注入回AndroidManagerService
中,这样接管了系统启动、管理service等操做。
加载插件APK是经过PluginManager
的loadPlugin
方法,以下图所示,此处就是将apk拆开,解析,读取,加载,组装为LoadedPlugin并保存,以方便后面管理与使用。
此处对Apk进行了复杂的解析、加载、合并等操做,大体流程以下:
AssetManager
建立Resource
对象,平台用AssetManager建立出Resource,判断是否和宿主Apk合并资源。 那么是时候启动插件Activity了。经过startActivity即可以启动。从上面的流程咱们知道启动是从Instrumentation.execStartActivity();
开始的,而系统的Instrumentation
已经被VAInstrumentation
替换,其中VAInstrumentation
重写了几个关键方法:
没错,以下图,在启动Activity的入口处,VirtualApk拦截了请求,而后根据Intent的参数,去匹配plugin中的Activity坑位,以后替换Intent中的Activity,以此来达到欺骗系统的效果。
可是,由于这个Activity的对象了实际上并不存在,最终咱们须要启动的是实现了的targetActivity,因此须要拦截Instrumentation
的第二个方法newActivity
,由于在ActivityTread
的performLaunchActivity
中,会调用Instrumentation
的 newActivity
。
在newActivity
中,以下图,类没有找到时(坑位类确定找不到啦),那么就去获取本来保存在Intent的目标Activity,而后调用建立VAInstrumentation
时保存的Instrumentation
(mBase)去建立Activity。
Activity虽然建立好了,可是它对应的资源和context都还不对,因此咱们须要在Activity的OnCreate以前完成好Resource的注入。前面加载Apk时,这些资源都保存在Plugin
中。因此咱们拦截callActivityOnCreate
方法,以下图,将Activity的Context、Application、Reource,都替换成Plugin中对应的对象。一个Activity就这样绕过AndroidManifest启动起来了。
startService启动Service时,还记得上面咱们经过ActivityManagerProxy
生成IActivityManager
吗?它主要拦截了Service相关启动和中止等方法,而后将其都转化为对应的startService方法,指向代理Service。由于startService方法的特性,他们最终都会在代理Service的onStartCommand
中被统一处理。
以下图,是ActivityManagerProxy
,其中invoke拦截了全部相关的服务请求,并作了转化处理,下面以startService为例。
startService这里,主要即是提取本来目标service信息,而后转化为代理Service,发送到代理Service,下方图片为启动流程和转化流程。
以下图,在代理service中,根据请求类型,代理service会经过classLoader加载来建立service,并操做其attach、onCreate、onStartCommand等,让service工做起来。
自此Activity和Service都成功启动了,是否是对插件化有了不同的了解?
允许这里插入这一块,安利下Virtual中的AndroidStub模块,以下图
由于都用反射很浪费性能,因此有了AndroidStub
,它是用来欺骗编译器的。正常状况下你想操做ActivityThread
就会出现以下图状况,由于它是一个@hide
类,这时候除了反射获得ActivityThread
,你还须要再反射须要执着方法才能执行,这在必定程度会损耗一些性能。
可是以下图,VirtualApk经过AndroidStub
,模拟源码建立了如ActivityThread
类,这里你就能够如图正常使用ActivityThread
了,而AndroidStub
中的ActivityThread
,其实只是定义了和原码中一摸同样的方法,并无其余实现。
由于CoreLibrary
依赖AndroidStub
使用的是provided
,由于provided
依赖是不打包依赖包,而是运行时提供,因此成功欺骗了编辑器,以此提升了性能。很神奇吧?
终于结束了,若是你看到了这里,相信你是一个颇有耐心的同志!固然插件化仍是其余实现方式,如Replugin,只Hook住了ClassLoader,流程更加复杂,若有什么建议和疑问,欢迎留言讨论。
VirtualApk:github.com/didi/Virtua…
我的github:github.com/CarGuo