一、什么是Android插件化
个人认为就是把好几个不同功能apk免安装的集成在一个apk中使用,apk之间相互解藕,相互独立,大体上可以分为两类:
一类,插件可以独立运行,不依赖于宿主。
***********相关知识************
DexClassLoader可以加载任何路径的apk/dex/jar
PathClassLoader只能加载/data/app中的apk,也就是已经安装到手机中的apk。这个也是PathClassLoader作为默认的类加载器的原因,因为一般程序都是安装了,在打开,这时候
PathClassLoader就去加载指定的apk(解压成dex,然后在优化成odex)就可以了
http://blog.csdn.net/jiangwei0910410003/article/details/41384667
AssetManager资源管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
protected void loadResources() { try { AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, mDexPath); mAssetManager = assetManager; } catch (Exception e) { e.printStackTrace(); } Resources superRes = super.getResources(); mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); mTheme = mResources.newTheme(); mTheme.setTo(super.getTheme()); } |
说明:加载的方法是通过反射,通过调用AssetManager中的addAssetPath方法,我们可以将一个apk中的资源加载到Resources中,由于addAssetPath是隐藏api我们无法直接调用,所以只能通过反射,传递的路径可以是zip文件也可以是一个资源目录,而apk就是一个zip,所以直接将apk的路径传给它,资源就加载到AssetManager中了,然后再通过AssetManager来创建一个新的Resources对象,这个对象就是我们可以使用的apk中的资源了,这样我们就获取到了插件中资源。
app下载插件存放指定的文件,通过检索文件的方式发现插件,使用classloader根据路径+apk名加载插件。
DexClassLoader loader = new DexClassLoader(libPath, filesDir,filesDir, getClassLoader()); |
DL 动态加载框架 ( 2014 年底)
https://github.com/singwhatiwanna/dynamic-load-apk
DL支持的功能
1、plugin无需安装即可由宿主调起。
2、支持用R访问plugin资源
3、plugin支持Activity和
FragmentActivity(未来还将支持其他组件)
4、基本无反射调用
5、插件安装后仍可独立运行从而便于调试
6、支持3种plugin对host的调用模式:
(1)无调用(但仍然可以用反射调用)。
(2)部分调用,host可公开部分接口供plugin调用。 这前两种模式适用于plugin开发者无法获得host代码的情况。
(3)完全调用,plugin可以完全调用host内容。这种模式适用于plugin开发者能获得host代码的情况。
7、只需引入DL的一个jar包即可高效开发插
件,DL的工作过程对开发者完全透明
8、支持android2.x版本
DL框架原理
动态加载主要有两个需要解决的复杂问题:资源的访问和activity生命周期的管理,除此之外,还有很多坑爹的小问题,而DL框架很好地解决了这些问题。需要说明的一点是,我们不可能调起任何一个未安装的apk,这在技术上是很难实现的,我们调起的apk必须受某种规范的约束,只有在这种约束下开发的apk,我们才能将其调起。
DroidPlugin ( 2015 年 8 月)
https://github.com/DroidPluginTeam/DroidPlugin
DroidPlugin 是 360 手机助手实现的一种插件化框架,它可以直接运行第三方的独立 APK 文件,完全不需要对 APK 进行修改或安装。一种新的插件机制,一种免安装的运行机制,是一个沙箱(但是不完全的沙箱。就是对于使用者来说,并不知道他会把 apk 怎么样), 是模块化的基础。
DroidPlugin 插件机制 :它可以在无需安装、修改的情况下运行APK文件,此机制对改进大型APP的架构,实现多团队协作开发具有一定的好处。
定义:
HOST程序:插件的宿主。
插件:免安装运行的APK
限制和缺陷:
无法在插件中发送具有自定义资源的Notification,例如:
a. 带自定义RemoteLayout的Notification
b. 图标通过R.drawable.XXX指定的通知(插件系统会自动将其转化为Bitmap)
无法在插件中注册一些具有特殊Intent Filter的Service、Activity、BroadcastReceiver、ContentProvider等组件以供Android系统、已经安装的其他APP调用。
缺乏对Native层的Hook,对某些带native代码的apk支持不好,可能无法运行。比如一部分游戏无法当作插件运行。
特点:
支持Androd 2.3以上系统
插件APK完全不需做任何修改,可以独立安装运行、也可以做插件运行。要以插件模式运行某个APK,你无需重新编译、无需知道其源码。
插件的四大组件完全不需要在Host程序中注册,支持Service、Activity、BroadcastReceiver、ContentProvider四大组件
插件之间、Host程序与插件之间会互相认为对方已经"安装"在系统上了。
API低侵入性:极少的API。HOST程序只是需要一行代码即可集成Droid Plugin
超强隔离:插件之间、插件与Host之间完全的代码级别的隔离:不能互相调用对方的代码。通讯只能使用Android系统级别的通讯方法。
支持所有系统API
资源完全隔离:插件之间、与Host之间实现了资源完全隔离,不会出现资源窜用的情况。
实现了进程管理,插件的空进程会被及时回收,占用内存低。
插件的静态广播会被当作动态处理,如果插件没有运行(即没有插件进程运行),其静态广播也永远不会被触发。
Small ( 2015 年底)
https://github.com/wequick/Small
Small 是一种实现轻巧的跨平台插件化框架,基于“轻量、透明、极小化、跨平台”的理念
优点如下:
所有插件支持内置宿主包中。
2.插件的编码和资源文件的使用与普通开发应用没有差别。
3.通过设定 URI ,宿主以及 Native 应用插件,Web 插件,在线网页等能够方便进行通信。
4.支持 Android 、 iOS 、和 Html5 ,三者可以通过同一套 Javascript 接口实现通信。
缺点如下:
暂不支持 Service 的动态注册,不过这个可以通过将 Service 预先注册在宿主的 AndroidManifest.xml 文件中进行规避,因为 Service 的更新频率通常非常低。
VirtualAPK (2017年 6 月 )
https://github.com/didi/VirtualAPK
VirtualAPK 是滴滴开源的一套插件化框架,支持几乎所有的 Android 特性,四大组件方面。
原理:
VirtualAPK 对插件没有额外的约束,原生的 apk 即可作为插件。插件工程编译生成 apk后,即可通过宿主 App 加载,每个插件 apk 被加载后,都会在宿主中创建一个单独的 LoadedPlugin 对象。如下图所示,通过这些 LoadedPlugin 对象,VirtualAPK 就可以管理插件并赋予插件新的意义,使其可以像手机中安装过的 App 一样运行。
合并宿主和插件的ClassLoader 需要注意的是,插件中的类不可以和宿主重复
合并插件和宿主的资源 重设插件资源的 packageId,将插件资源和宿主资源合并
去除插件包对宿主的引用 构建时通过 Gradle 插件去除插件对宿主的代码以及资源的引用
特性如下:
四大组件均不需要在宿主manifest中预注册,每个组件都有完整的生命周期。
1.Activity:支持显示和隐式调用,支持Activity的theme和LaunchMode,支持透明主题;
2.Service:支持显示和隐式调用,支持Service的start、stop、bind和unbind,并支持跨进程bind插件中的Service;
3.Receiver:支持静态注册和动态注册的Receiver;
4.ContentProvider:支持provider的所有操作,包括CRUD和call方法等,支持跨进程访问插件中的Provider。
5.自定义View:支持自定义 View,支持自定义属性和style,支持动画;
6.PendingIntent:支持PendingIntent以及和其相关的Alarm、Notification和AppWidget;
7.支持插件Application以及插件manifest中的meta-data;
8.支持插件中的so。
优秀的兼容性
兼容市面上几乎所有的Android手机,这一点已经在滴滴出行客户端中得到验证。
资源方面适配小米、Vivo、Nubia 等,对未知机型采用自适应适配方案。
极少的 Binder Hook,目前仅仅 hook了两个Binder:AMS和IContentProvider,hook 过程做了充分的兼容性适配。
插件运行逻辑和宿主隔离,确保框架的任何问题都不会影响宿主的正常运行。
RePlugin (2017 年 7 月)
https://github.com/Qihoo360/RePlugin
RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案,由360手机卫士的RePlugin Team研发,也是业内首个提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。
其主要优势有:
极其灵活:主程序无需升级(无需在Manifest中预埋组件),即可支持新增的四大组件,甚至全新的插件
非常稳定:Hook点仅有一处(ClassLoader),无任何Binder Hook!如此可做到其崩溃率仅为“万分之一”,并完美兼容市面上近乎所有的Android ROM
特性丰富:支持近乎所有在“单品”开发时的特性。包括静态Receiver、Task-Affinity坑位、自定义Theme、进程坑位、AppCompat、DataBinding等
易于集成:无论插件还是主程序,只需“数行”就能完成接入
管理成熟:拥有成熟稳定的“插件管理方案”,支持插件安装、升级、卸载、版本管理,甚至包括进程通讯、协议版本、安全校验等
数亿支撑:有360手机卫士庞大的数亿用户做支撑,三年多的残酷验证,确保App用到的方案是最稳定、最适合使用的
截止2017年6月底,RePlugin的:
目前360公司几乎所有的亿级用户量的APP,以及多款主流第三方APP,都采用了RePlugin方案。
特性
怎么让一个没有安装的apk运行起来呢?资源又该如何引用?
android插件化目前没有一个完美的开源的解决方案,开源的框架有很多,但都存在着一些问题,对于小型的插件app应用开发足矣,但是对于大型项目而言不那么适用。
我们知道, 一个apk中包含三种文件,dex、resouce、mainfest,实现插件化无非就是对apk中这些文件的引用关联,以及使插件的组件用有与正常组件相同生命周期等;实现方式分为两种,一种是,通过反射机制,代理模式为组件构造伪生命周期,或者是注入context对象,实现接口的方式,此种方式必然要用到DexClassloader与AssetManager,一个加载dex,一个加载resouce以此达到调用插件方法的目的,这种方式或多或少对apk有一定的侵入性,插件为配合宿主的调用要特定规则下编写,某些编写的插件还不可以独立运行,此方法简单易用,对于小型插件来说足矣。第二种方式是,hook系统API,为插件新建一个进程运行,或者是将一个进程有效分割使用,通过拦截系统执行代码,替换成自己的代码,以欺骗的系统方式达到插件的调用,此方法启动的apk有有效的进程管理,组件有完整的生命周期。
插件化可以让一个app的功能模块化,比如说一个app需要一个扫描二维码的功能,只要加载一个二维码插件就可以了,对用户来说可以随性选择需要的插件,动态加载,有效的减少了app的体积,对开发人员来说,不必关注太多关于宿主app细节,只需互相为对方提供一个接口,就可以达到交互的目的。一定程度上做到开发隔离,从而达到并行开发的目的。而且对于部分模块的bug不会影响到整个app,修复bug之后也可以迅速部署,不必更新整个客户端,同时也减少了app运行时占用的内存空间,做到了按需加载。
从应用层面来讲,目前应用最多的是广告类插件,以插件形式运行的广告不会对宿主app造成太多侵入性,而且一份插件适用于所有app。而对于业务繁多的项目,使用插件化可以有效提高开发效率等诸多优点。从技术层面讲,如果说要保证插件与宿主达到和一个完整的完整app一样的使用效果,这将会考虑到很多问题,目前这方面做的较好的是淘宝的Atlas。
android插件化优点很多,但是其局限性也很多,使用起来可能会遇到很多问题。