常煜 邓飞
摘要:Android动态加载技术指应用在运行的时候通过加载一些本地不存在的可执行文件实现一些特定的功能,而且这些可执行文件是可以替换的。通过对Android动态加载技术的原理进行分析,解决关键性问题,实现Android动态加载技术。该技术可以解决Android应用开发到一定规模时APK安装包过大,功能模块过多,没有办法选择性加载所需模块的问题,既可以显著提高应用新版本的覆盖率,也可以用来修复紧急BUG。
关键词:Android;动态加载;APK
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2016)23-0049-02
Abstract: Android dynamic loading technology refers to loading certain executable and substitutable files that do not exist locally while the application is running, thus specific functions being achieved. The implementation of this technology can be realized through the analysis of its principles and the solving of key problems. Problems like too large APK installation package, too many function modules, and difficulties in selectively loading required modules when Android application is developed to a certain scale can be well resolved by this technology. The coverage of the new version of the application can be significantly enlarged and emergency bug can also be fixed through this technology.
Key words: Android; dynamic loading; APK
1 背景
动态加载[1](Dynamic Loading)是一种程序运行机制,能让计算机程序在运行时(而不是编译时)装载库(或者其他二进制对象)到内存中,然后检索库中函数和变量的地址,并执行这些函数或访问这些变量,且能在不需要时将库从内存中卸载。与静态链接相比,动态加载具有增加程序灵活性、节约内存空间的优点。Android动态加载[2]是指应用在运行的时候通过加载一些本地不存在的可执行文件实现一些特定的功能,而且这些可执行文件是可以替换的。Android中动态加载的核心思想是动态调用外部的dex文件,极端的情况下,Android APK自身带有的dex文件只是一个程序的入口(或者说是空壳),所有的功能都是通过从服务器下载最新的dex文件完成[3]。
Android动态加载技术能够解决Android应用开发到一定规模时APK安装包过大,功能模块过多,没有办法选择性加载所需模块的问题,达到不安装新APK就升级APP的目的,既可以显著提高应用新版本的覆盖率,也可以减少服务器对旧版本接口兼容的压力,同时也可以用来修复一些紧急BUG。国内百度、腾讯、阿里巴巴等大公司均对此有深入的研究和应用,但一般仅在内部使用,而且这种开发方式不是官方推荐的,也不是目前主流Android开发方式,相关文档及开发较少,大部分开发者实现较为困难。
目前,Android项目中动态加载技术按照加载的可执行文件的不同大致可以分为两种:动态加载so库和动态加载dex/jar/apk文件。动态加载so库一般用来完成对一些性能比较有需求的工作,比如Bitmap的解码、图片高斯模糊处理等;动态加载dex/jar/apk文件即在Android中动态加载由Java代码编译而来的dex包并执行其中的业务逻辑[4],相比前者较为容易实现,大部分Android动态加载技术均使用此种方式,本文也采用此种方式实现Android动态加载。
本文介绍了Android动态加载技术的原理实现,提供一种合理的Android动态加载实现方式,解决部分开发者因无相关资料文档而无法实现动态加载的问题。
2 动态加载技术的原理与实现
2.1 动态加载技术原理
Android动态加载技术基本原理是在程序运行时加载一些外部的可执行的文件,然后调用这些文件的某个方法执行业务逻辑。因为文件是可执行的,出于安全问题,Android并不允许直接加载手机外部存储这类noexec(不可执行)存储路径上的可执行文件[5]。在Android应用中调用它们前,都要把这些可执行文件拷贝到data/packagename/内部储存文件路径,确保库不会被第三方应用恶意修改成拦截,然后再将这些可执行文件加载到当前的运行环境并调用需要的方法执行相应的逻辑,从而实现动态调用。流程图如下:
2.2 动态加载技术的实现
首先需要获得想要动态加载的可执行文件。通过JDK的编译命令javac把Java代码编译成.class文件,再使用jar命令把.class文件封装成.jar文件,再用Android SDK的DX工具把.jar文件优化成.dex文件,即需要的可执行文件。
与JVM不同,Android的虚拟机不能用ClassLoader类直接加载.dex,而是需要用DexClassLoader类。DexClassLoader类是ClassLoader类的子类,可以加载jar/apk/dex,可以从SD卡中加载未安装的apk。但DexClassLoader并不能直接加载外部存储的.dex文件,而是要先拷贝到内部存储里。实际使用DexClassLoader的代码如下:
调用成功后,就可以成功从外部路径动态加载一个.dex文件,并执行里面的代码逻辑,但是还不能直接启动插件(指经过处理的dex或者apk)的Activity[6]。Activity等组件需要在Manifest中注册后才能以标准Intent的方式启动,通过ClassLoader加载并实例化的Activity实例只是一个普通的Java对象[7],能调用对象的方法,但是它没有生命周期,而且Activity等系统组件是需要Android的上下文环境的(Context等资源),没有这些东西Activity根本无法工作。想要使用插件里的Activity需要解决两个问题:如何使插件APK里的Activity具有生命周期;如何使插件APK里的Activity具有上下文环境(使用R资源)。首先要处理插件Activity的生命周期,因为一个Activity的启动,如果不采用标准的Intent方式,没有经历过Android系统Framework层级的一系列初始化和注册过程,它的生命周期方法是不会被系统调用的。可以通过在主项目里创建一个ProxyActivity,再由它去代理调用插件Activity的生命周期方法。用ProxyActivity(一个标准的Activity实例)的生命周期同步控制插件Activity的生命周期。同步的方式既可以在ProxyActivity生命周期里用反射调用插件Activity相应生命周期的方法,又可以把插件Activity的生命周期抽象成接口[8],在ProxyActivity的生命周期里调用。然后在插件Activity里使用R资源,因为res里的每一个资源都会在R.java里生成一个对应的Integer类型的id,APP启动时会先把R.java注册到当前的上下文环境,在代码里以R文件的方式使用资源时正是通过使用这些id访问res资源,然而插件的R.java并没有注册到当前的上下文环境,所以插件的res资源也就无法通过id使用。想要解决此问题,可以通过获取一个AssetManager实例,使用其“addAssetPath”方法加载APK里的资源,再使用DisplayMetrics、Configuration、CompatibilityInfo实例一起创建所需要的Resources实例。访问插件APK里res资源的关键代码如下:
3 结束语
本文分析了Android动态加载的原理、过程以及实现,解决了Android动态加载中遇到的如何使插件APK里的Activity具有生命周期、如何使插件APK里的Activity具有上下文环境(使用R资源)等关键问题。将此技术应用到Android应用中,可以解决Android应用开发到一定规模时APK安装包过大,功能模块过多,没有办法选择性加载所需模块的问题,达到不安装新APK就升级APP的目的,既可以显著提高应用新版本的覆盖率,也可以减少服务器对旧版本接口兼容的压力,同时也可以用来修复一些紧急BUG。
参考文献:
[1] David A. The Linux Documentation Project [EB/OL]. http://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html.
[2] 任玉刚. Android开发艺术探索[M]. 北京: 电子工业出版社, 2015.
[3] Bill Phillips, Brian Hardy. Android programming: the big nerd ranch guide[M]. 北京: 人民邮电出版社, 2014.
[4] Meier R. Professional Android 4 Application Development[M]. 北京: 清华大学出版社, 2013.
[5] Hervé Guihot. Pro Android Apps Performance Optimization[M]. 北京: 人民邮电出版社, 2012.
[6] 林学森. 深入理解Android内核设计思想[M]. 北京: 人民邮电出版社, 2014.
[7] Horstmann C S, Cornell G. Core Java V[M]. 北京: 人民邮电出版社, 2013.
[8] Bruce Eckel. Thinking In java[M]. 北京: 机械工业出版社, 2007.