一、组件化
组件化,就是把APP拆分成不同功能模块,形成独立组件,让宿主调用。
组件化不一定是插件化,组件化是一个更大的概念:把模块解耦,组件之间代码不依赖,宿主可以依赖组件;而插件化则具体到了技术点上,宿主通过 动态加载 来调用组件,宿主不依赖组件,达到 完全解耦 的目的(比如图片缓存就可以看成一个组件被多个 App 共用)。
二、插件化
Android程序每次更新都要下载一个完整的apk,而很多时候软件只是更新了一个小功能而已,这样的话,就显得很麻烦。如果把android程序做成主程序+插件化的形式呢,这样才利于小功能的扩展(比如一般 App 的皮肤样式就可以看成一个插件)。
通过 gradle 配置的方式,将打 debug 包和 release 包分开。这样会有一个好处,开发一个模块,在 debug 的时候,可以打成一个 apk ,独立运行测试,可以完全独立于整个宿主 APP 的其他所有组件;待到要打 release 包的时候,再把这个模块作为一个 library ,打成 aar ,作为整个宿主 APP 的一部分。而 debug 和 release 的切换都是通过 gradle 配置,可以做到无缝切换。至于模块之间的跳转,可以用别名的方式,而不是用 Activity 和 Fragment 类名。这样所有的模块和宿主 APP 都是完全解耦的,彻底解决了并行开发的可能造成的交叉依赖等问题。
主要原理是:主要利用 Java ClassLoader 的原理,如 Android 的 DexClassLoader,可动态加载的内容包括 apk、dex、jar 等。如下
DexClassLoader pluginClassLoader =
new DexClassLoader(dexPath, optimizedDirectory, libraryPath, parentClassLoader);
插件化的优势:
- 适应并行开发,解耦各个模块,避免模块之间的交叉依赖,加快编译速度,从而提高并行开发效率。
- 满足产品随时上线的需求
- 修复因为我们对自己要求不严格而写出来的 bug。
- 插件化的结果:分为稳定的 release 版本和不稳定的 snapshot 版本,每个模块都高度解耦,没有交叉依赖,不会出现一个模块依赖了另一个模块,其中一个人改了这个模块的代码,对另一个模块造成影响。
淘宝的框架是用了osgi的bundle概念,整个应用框架生命周期完整。
插件化弊端:
每一个插件都是一个apk,插件多的时候管理起来也麻烦。
三、Android 动态加载机制
我们知道,apk必须安装才能运行,apk未安装是不能被直接调起来的,但是我们可以采用一个程序(称之为宿主程序)去动态加载apk文件并将其放在自己的进程中执行。
四、Android 动态升级、增量更新
Google I/O 上提及的 Smart App update,即应用增量升级,或者叫做差分升级的做法,并在新版本的Google Play中得到支持。
原理
其实增量升级的原理很简单,即首先将应用的旧版本Apk与新版本Apk做差分,得到更新的部分的补丁。 在用户下载了差分包之后,需要在手机端将他们组合起来。可以参考的做法是先将手机端的旧版本软件(多半在/data/下),复制到SD卡或者cache中,将它们和之前的差分patch进行组合,得到一个新版本的apk应用。如果做过android手机OTA升级的同学应该注意到,在update.zip中的patch文件夹中有需要与系统文件同名但是以xxx.p 为后缀的文件,他们就是生成的差分patch文件。
前提
增量升级成功的前提是,用户手机端必须有能够让你拷贝出来且与你服务器用于差分的版本一致的apk,这样就存在,例如,系统内置的apk无法获取到,无法进行增量升级;对于某些与你差分版本一致,但是内容有过修改的(比如破解版apk),这样也是无法进行增量升级的,为了防止合成补丁错误,最好在补丁合成前对旧版本的apk进行sha1sum校验,保证基础包的一致性。
弊端:
- 增量更新对于更新频繁的软件来说,版本越多,需要做的差分包就越多,这样每次更新验证的工作量会非常大
增量升级是以两个应用版本之间的差异来生成补丁的,你无法保证用户每次的及时升级到最新,所以你必须对你所发布的每一个版本都和最新的版本作差分,以便使所有版本的用户都可以差分升级,这样操作相对于原来的整包升级较为繁琐,不过可以通过自动化的脚本批量生成。
五、Class Loader类加载的方式:DexClassLoader 、 PathClassLoader与URLClassLoader
什么样的Class可以Load?
Java 里面直接把 .class 文件打包到 .jar 文件里面就可以了,但是 Android 的 Dalvik VM 是不认 Java 的 byte code 的,所以不能直接这么打包,而要用 dx 工具转成 Dalvik byte code 才可以。当然,dx 工具转了之后,jar 包里面就不是 .class 文件了,而是 .dex 文件。
Class Loader 的选择主要包括DexClassLoader
、PathClassLoader
和URLClassLoader
。
- DexClassLoader
可以加载文件系统上的jar、dex、apk
File dexOutputDir = context.getDir("dex", 0);
DexClassLoader cl =
new DexClassLoader(jarFile.toString(), "/sdcard/test", null, ClassLoader.getSystemClassLoader());
- PathClassLoader
可以加载/data/app
目录下的apk,这也意味着,它只能加载已经安装的apk。其它位置的文件加载的时候都会出现ClassNotFoundException
PathClassLoader cl =
new PathClassLoader(jarFile.toString(), "/data/app/", ClassLoader.getSystemClassLoader());
- URLClassLoader
可以加载java中的jar,但是由于dalvik不能直接识别jar,所以此方法在android中无法使用,尽管还有这个类。
dex和apk是可以直接加载的,因为它们都是或者内部有dex文件,而原始的jar是不行的,必须转换成dalvik所能识别的字节码文件,转换工具可以使用android sdk中platform-tools目录下的dx。
//转换命令 :
dx --dex --output=dest.jar src.jar
六、热门Android插件化框架
Small、Dynamic-load-apk、ACDD、DroidPlugin(360手机助手的开源框架)
(一)、Small官网
官网
Small插件化方案适用于将一个APK拆分为多个公共库插件、业务模块插件的场景(如:插件化加载dex、资源拆分、hook)。
(二)、Dynamic-load-apk
这个项目实现了一部分的动态加载,原理是 DexClassLoader 加 Activity 代理,可以看看。即在容器中注册几个代理的 Activity,启动插件的 Activity 时实际启动的都是代理的 Activity,这样就解决了 Activity 必须注册的问题。当然这个项目里也有不少问题没解决,有兴趣可以加入他们。
(三)、AndroidDynamicLoader)
这是点评一个工程师介绍的方式,和上面不同的是:他不是用代理 Activity 的方式实现而是用 Fragment 以及 schema 的方式实现
网友评论