标签:插件化

插件化

【插件化】开篇总述

前段时间因为工作需要开发环境安全SDK,在开发该SDK的过程中有一个子功能就是检测App是否运行在可信环境中,其中一项就是对虚拟环境的检测,也即多开/分身类App的检测。因此调研了下这类App的实现原理,发现其原理本质上就是通过插件化技术达到在多开App的沙盒环境中免安装运行用户App。检测多开App其实很简单,但是多开App背后的原理插件化却没那么简单甚至可以说是非常复杂,不过我认为也是非常值得我们学习的。首先插件化和热修复一样本身属于安卓应用层安全的范畴,另外在学习插件化的过程中我发现如果能够把插件化的原理了解透彻,那么你将会对安卓framework层的运行机制,尤其是和四大组件相关的运行机制有着非常深刻的理解。个人觉得只要你从事的是和安卓相关的工作,不管是应用层App开发,还是安卓ROM开发亦或是安卓安全,插件化技术以及多开技术绝对是值得你去学习和了解的。 首先从效果来看多开App的目的是达到在不安装插件Apk到手机系统的情况下,在多开App中运行该插件Apk。另外运行的插件Apk和多开App本身是无任何关联的,比如可以在平行空间这个多开App内运行微信,微信和平行空间这个App在开发时肯定是无任何关联的,也就是说插件Apk即可作为插件被多开App调起,同时本身也是一个独立的Apk。从开发者的角度来看,在开发插件Apk的过程中对宿主完全无依赖,无感知。 从这个效果来看我们可以提炼出三大关键信息: 免安装 运行在宿主App模拟的虚拟环境下 插件和宿主相互独立,插件对宿主无依赖,无感知 这3大特征实际上就对应了要开发一个插件化框架需要解决的几大问题: 免安装:意味着插件Apk的Manifest文件不能被安卓系统解析,插件中声明的组件信息对安卓系统而言是无效的。而四大组件的使用必须在Manifest清单文件中声明,所以这里就有一个问题需要解决,如何保证宿主调用插件中的组件时不出现未声明异常,如启动插件中的Activity而不出现have you declared this activity in your androidmanifest.xml异常. 运行在宿主App虚拟环境内:意味着插件Apk是无自己的Context的。因为安卓应用程序和普通java代码程序的一个很大的区别就是安卓App在启动过程中会在负责启动App的系统framework层的代码的启动流程中赋值。被宿主App调起而非系统调起,意味着该插件App没走系统的启动流程,因此很多具备安卓特性的类的功能是不完备的,如插件自己的Context。Application。所以需要解决插件Apk Context,Application等问题 1和2合起来(未安装且由宿主调起):意味着插件Apk代码的加载和资源的加载是宿主解决。所以需要解决插件Apk代码和资源的加载问题 插件和宿主相互独立:意味着不能通过插件遵守宿主开发规约的形式来实现上述目的,这一条和第2条合起来意味着不能通过代理组件的形式来实现插件Apk中组件的生命周期同步。比如插件通过调用宿主代理Activity的形式来同步Activity生命周期。所以需要解决插件Apk中相关组件的生命周期的管理。 所以从上面可以看到一个成熟的插件化框架主要需要解决以下几大关键问题: 插件Apk代码的加载和资源的加载。也即插件Apk中类的加载和图片,字符串,布局文件等资源文件的加载。 安卓中特有类的创建以及与直接安装到系统上的功能的一致性,比如Application,Context,Resource等,这些类不是简单的new一个或通过Classloader加载创建后就能使用的,需要做到插件中的这些类在宿主中调起时和被安装到手机系统调起时其功能是等同的,而不是残缺的或不一致的,比如插件Apk的Activity在获取资源时应该获取到插件Apk自己的,而不是宿主的Resource指向的资源 插件Apk四大组件的生命周期的管理 要开发一个插件化框架,上述3大问题是最核心的问题,也是插件化的理论基础部分需要解决的核心问题,当然在实际开发实践过程中也会有很多其他小问题需要解决,比如Activty不同启动模式的解决,不同ROM版本的兼容性解决,多个插件环境隔离,多个插件之间的通信等。 但是上述3大问题是最核心的问题。解决上述3大核心问题基本上就能开发一个插件化框架的demo了。(当然要开发一款商用的高稳定性和兼容性的插件化框架还是很麻烦的)。 通过前面三大难题解决思路的讲解,其实你很容易发现,插件化框架的大的宗旨就是把原本需要在安卓framework层由系统做的事提前到应用层由宿主App做掉。 那么哪些事情需要宿主App替系统做掉呢?实际上主要还是前面提到的几个部分: 替系统“安装”插件Apk  替系统加载插件Apk的类和资源 替系统维护插件Apk的安卓特有类的功能的“正确性”,如Context/ContextImpl,Application,Resource等 这就引出了后续插件化系列的打算写的其他文章,暂定以下内容: 从插件化看安卓系统Apk安装的流程和本质 从插件化看安卓系统Activity的继承结构与启动流程 从插件化看安卓系统资源加载流程和原理 自己动手写一个插件化框架demo 因为类的加载和Application启动的细节在热修复相关专题中已经分析过了,所以基本上前3大块内容弄清楚之后就可以自己手撸一个支持免安装仅包含Activity组件的插件Apk的插件化框架了。若想支持其他3大组件也可以依此思路去分析,而且理论上其他三大组件的支持要比Activity简单些。 另外插件化技术领域也开源了很多优秀的框架,个人比较推荐如下2个: https://github.com/didi/VirtualAPK https://github.com/Qihoo360/RePlugin 其中RePlugin从技术原理上来讲不属于插件和宿主/插件化lib完全无感知。只不过因为该框架提供了和插件相关的gradle编译脚本在编译时自动替接入者把这些事做了。从使用上达到了开发插件对宿主完全无感知。(用RePlugin团队的话来说就是开发插件像开发单品那样) 个人觉得插件化,热修复,Instan Run这3大应用场景是学习安卓framework层运行机制和原理的绝佳切入点,尽管网上有很多关于安卓framework层源码分析的文章,比如老罗的安卓之旅,但是绝大多数文章都是直接分析源码,长篇大论,如果你不是安卓系统/ROM开发工程师,不具备和framework层开发打交道的经验,那么很容易只见树木不见森林,抓不住重点,不知道哪些细节是很重要的,哪些细节是不重要的,导致走马观花,过后即忘。而这3大应用场景尤其是插件化需要对framework层的很多细节比较清楚。这也是为何我建议大家自己动手去写一个插件化框架的原因,当然不需要你去开发一个商用的兼容各个版本ROM的插件化框架,只需要保证能在你的测试机上免安装运行一个相对复杂的apk即可。当你完成这个任务之后,你对安卓系统framework层将会非常了解了。