我们很早开始就在Android项目中采用了动态加载技术,主要目的是为了达到让用户不用重新安装APK就能升级应用的功能(特别是SDK项目),这样一来不但可以大大提高应用新版本的覆盖率,也减少了服务器对旧版本接口兼容的压力,同时如果也可以快速修复一些线上的BUG。
这种技术并不是常规的Android开发方式,早期并没有完善的解决方案。从“不明觉厉”到稳定投入生产,一直以来我总想对此编写一些文档,这也是这篇日志的由来,没想到前前后后竟然拖沓着编辑了一年多,所以日志里有的地方思路可能有点衔接得不是很好,如果有修正建议请直接回复。
技术背景
通过服务器配置一些参数,Android APP获取这些参数再做出相应的逻辑,这是常有的事情。
比如现在大部分APP都有一个启动页面,如果到了一些重要的节日,APP的服务器会配置一些与时节相关的图片,APP启动时候再把原有的启动图换成这些新的图片,这样就能提高用户的体验了。
再则,早期个人开发者在安卓市场上发布应用的时候,如果应用里包含有广告,那么有可能会审核不通过。那么就通过在服务器配置一个开关,审核应用的时候先把开关关闭,这样应用就不会显示广告了;安卓市场审核通过后,再把服务器的广告开关给打开,以这样的手段规避市场的审核。
道高一尺魔高一丈。安卓市场开始扫描APK里面的Manifest甚至dex文件,查看开发者的APK包里是否有广告的代码,如果有就有可能审核不通过。
通过服务器怕配置开关参数的方法行不通了,开发者们开始想,“既然这样,能不能先不要在APK写广告的代码,在用户运行APP的时候,再从服务器下载广告的代码,运行,再现实广告呢?”。答案是肯定的,这就是动态加载:
在程序运行的时候,加载一些程序自身原本不存在的可执行文件并运行这些文件里的代码逻辑。
看起来就像是应用从服务器下载了一些代码,然后再执行这些代码!
传统PC软件中的动态加载技术
动态加载技术在PC软件领域广泛使用,比如输入法的截图功能。刚刚安装好的输入法软件可能没有截图功能,当你第一次使用的时候,输入法会先从服务器下载并安装截图软件,然后再执行截图功能。
此外,许多的PC软件的安装目录里面都有大量的DLL文件(Dynamic Link Library),PC软件则是通过调用这些DLL里面的代码执行特定的功能的,这就是一种动态加载技术。
熟悉Java的同学应该比较清楚,Java的可执行文件是Jar,运行在虚拟机上JVM上,虚拟机通过ClassLoader加载Jar文件并执行里面的代码。所以Java程序也可以通过动态调用Jar文件达到动态加载的目的。
Android应用的动态加载技术
Android应用类似于Java程序,虚拟机换成了Dalvik/ART,而Jar换成了Dex。在Android APP运行的时候,我们是不是也可以通过下载新的应用,或者通过调用外部的Dex文件来实现动态加载呢?
然而在Android上实现起来可没那么容易,如果下载一个新的APK下来,不安装这个APK的话可不能运行。如果让用户手动安装完这个APK再启动,那可不像是动态加载,纯粹就是用户安装了一个新的应用,然后再启动这个新的应用(这种做法也叫做“静默安装”)。
动态调用外部的Dex文件则是完全没有问题的。在APK文件中往往有一个或者多个Dex文件,我们写的每一句代码都会被编译到这些文件里面,Android应用运行的时候就是通过执行这些Dex文件完成应用的功能的。虽然一个APK一旦构建出来,我们是无法更换里面的Dex文件的,但是我们可以通过加载外部的Dex文件来实现动态加载,这个外部文件可以放在外部