常煜 鄧飛
摘要:Android動(dòng)態(tài)加載技術(shù)指應(yīng)用在運(yùn)行的時(shí)候通過(guò)加載一些本地不存在的可執(zhí)行文件實(shí)現(xiàn)一些特定的功能,而且這些可執(zhí)行文件是可以替換的。通過(guò)對(duì)Android動(dòng)態(tài)加載技術(shù)的原理進(jìn)行分析,解決關(guān)鍵性問(wèn)題,實(shí)現(xiàn)Android動(dòng)態(tài)加載技術(shù)。該技術(shù)可以解決Android應(yīng)用開(kāi)發(fā)到一定規(guī)模時(shí)APK安裝包過(guò)大,功能模塊過(guò)多,沒(méi)有辦法選擇性加載所需模塊的問(wèn)題,既可以顯著提高應(yīng)用新版本的覆蓋率,也可以用來(lái)修復(fù)緊急BUG。
關(guān)鍵詞:Android;動(dòng)態(tài)加載;APK
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):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 背景
動(dòng)態(tài)加載[1](Dynamic Loading)是一種程序運(yùn)行機(jī)制,能讓計(jì)算機(jī)程序在運(yùn)行時(shí)(而不是編譯時(shí))裝載庫(kù)(或者其他二進(jìn)制對(duì)象)到內(nèi)存中,然后檢索庫(kù)中函數(shù)和變量的地址,并執(zhí)行這些函數(shù)或訪問(wèn)這些變量,且能在不需要時(shí)將庫(kù)從內(nèi)存中卸載。與靜態(tài)鏈接相比,動(dòng)態(tài)加載具有增加程序靈活性、節(jié)約內(nèi)存空間的優(yōu)點(diǎn)。Android動(dòng)態(tài)加載[2]是指應(yīng)用在運(yùn)行的時(shí)候通過(guò)加載一些本地不存在的可執(zhí)行文件實(shí)現(xiàn)一些特定的功能,而且這些可執(zhí)行文件是可以替換的。Android中動(dòng)態(tài)加載的核心思想是動(dòng)態(tài)調(diào)用外部的dex文件,極端的情況下,Android APK自身帶有的dex文件只是一個(gè)程序的入口(或者說(shuō)是空殼),所有的功能都是通過(guò)從服務(wù)器下載最新的dex文件完成[3]。
Android動(dòng)態(tài)加載技術(shù)能夠解決Android應(yīng)用開(kāi)發(fā)到一定規(guī)模時(shí)APK安裝包過(guò)大,功能模塊過(guò)多,沒(méi)有辦法選擇性加載所需模塊的問(wèn)題,達(dá)到不安裝新APK就升級(jí)APP的目的,既可以顯著提高應(yīng)用新版本的覆蓋率,也可以減少服務(wù)器對(duì)舊版本接口兼容的壓力,同時(shí)也可以用來(lái)修復(fù)一些緊急BUG。國(guó)內(nèi)百度、騰訊、阿里巴巴等大公司均對(duì)此有深入的研究和應(yīng)用,但一般僅在內(nèi)部使用,而且這種開(kāi)發(fā)方式不是官方推薦的,也不是目前主流Android開(kāi)發(fā)方式,相關(guān)文檔及開(kāi)發(fā)較少,大部分開(kāi)發(fā)者實(shí)現(xiàn)較為困難。
目前,Android項(xiàng)目中動(dòng)態(tài)加載技術(shù)按照加載的可執(zhí)行文件的不同大致可以分為兩種:動(dòng)態(tài)加載so庫(kù)和動(dòng)態(tài)加載dex/jar/apk文件。動(dòng)態(tài)加載so庫(kù)一般用來(lái)完成對(duì)一些性能比較有需求的工作,比如Bitmap的解碼、圖片高斯模糊處理等;動(dòng)態(tài)加載dex/jar/apk文件即在Android中動(dòng)態(tài)加載由Java代碼編譯而來(lái)的dex包并執(zhí)行其中的業(yè)務(wù)邏輯[4],相比前者較為容易實(shí)現(xiàn),大部分Android動(dòng)態(tài)加載技術(shù)均使用此種方式,本文也采用此種方式實(shí)現(xiàn)Android動(dòng)態(tài)加載。
本文介紹了Android動(dòng)態(tài)加載技術(shù)的原理實(shí)現(xiàn),提供一種合理的Android動(dòng)態(tài)加載實(shí)現(xiàn)方式,解決部分開(kāi)發(fā)者因無(wú)相關(guān)資料文檔而無(wú)法實(shí)現(xiàn)動(dòng)態(tài)加載的問(wèn)題。
2 動(dòng)態(tài)加載技術(shù)的原理與實(shí)現(xiàn)
2.1 動(dòng)態(tài)加載技術(shù)原理
Android動(dòng)態(tài)加載技術(shù)基本原理是在程序運(yùn)行時(shí)加載一些外部的可執(zhí)行的文件,然后調(diào)用這些文件的某個(gè)方法執(zhí)行業(yè)務(wù)邏輯。因?yàn)槲募强蓤?zhí)行的,出于安全問(wèn)題,Android并不允許直接加載手機(jī)外部存儲(chǔ)這類noexec(不可執(zhí)行)存儲(chǔ)路徑上的可執(zhí)行文件[5]。在Android應(yīng)用中調(diào)用它們前,都要把這些可執(zhí)行文件拷貝到data/packagename/內(nèi)部?jī)?chǔ)存文件路徑,確保庫(kù)不會(huì)被第三方應(yīng)用惡意修改成攔截,然后再將這些可執(zhí)行文件加載到當(dāng)前的運(yùn)行環(huán)境并調(diào)用需要的方法執(zhí)行相應(yīng)的邏輯,從而實(shí)現(xiàn)動(dòng)態(tài)調(diào)用。流程圖如下:
2.2 動(dòng)態(tài)加載技術(shù)的實(shí)現(xiàn)
首先需要獲得想要?jiǎng)討B(tài)加載的可執(zhí)行文件。通過(guò)JDK的編譯命令javac把Java代碼編譯成.class文件,再使用jar命令把.class文件封裝成.jar文件,再用Android SDK的DX工具把.jar文件優(yōu)化成.dex文件,即需要的可執(zhí)行文件。
與JVM不同,Android的虛擬機(jī)不能用ClassLoader類直接加載.dex,而是需要用DexClassLoader類。DexClassLoader類是ClassLoader類的子類,可以加載jar/apk/dex,可以從SD卡中加載未安裝的apk。但DexClassLoader并不能直接加載外部存儲(chǔ)的.dex文件,而是要先拷貝到內(nèi)部存儲(chǔ)里。實(shí)際使用DexClassLoader的代碼如下:
調(diào)用成功后,就可以成功從外部路徑動(dòng)態(tài)加載一個(gè).dex文件,并執(zhí)行里面的代碼邏輯,但是還不能直接啟動(dòng)插件(指經(jīng)過(guò)處理的dex或者apk)的Activity[6]。Activity等組件需要在Manifest中注冊(cè)后才能以標(biāo)準(zhǔn)Intent的方式啟動(dòng),通過(guò)ClassLoader加載并實(shí)例化的Activity實(shí)例只是一個(gè)普通的Java對(duì)象[7],能調(diào)用對(duì)象的方法,但是它沒(méi)有生命周期,而且Activity等系統(tǒng)組件是需要Android的上下文環(huán)境的(Context等資源),沒(méi)有這些東西Activity根本無(wú)法工作。想要使用插件里的Activity需要解決兩個(gè)問(wèn)題:如何使插件APK里的Activity具有生命周期;如何使插件APK里的Activity具有上下文環(huán)境(使用R資源)。首先要處理插件Activity的生命周期,因?yàn)橐粋€(gè)Activity的啟動(dòng),如果不采用標(biāo)準(zhǔn)的Intent方式,沒(méi)有經(jīng)歷過(guò)Android系統(tǒng)Framework層級(jí)的一系列初始化和注冊(cè)過(guò)程,它的生命周期方法是不會(huì)被系統(tǒng)調(diào)用的。可以通過(guò)在主項(xiàng)目里創(chuàng)建一個(gè)ProxyActivity,再由它去代理調(diào)用插件Activity的生命周期方法。用ProxyActivity(一個(gè)標(biāo)準(zhǔn)的Activity實(shí)例)的生命周期同步控制插件Activity的生命周期。同步的方式既可以在ProxyActivity生命周期里用反射調(diào)用插件Activity相應(yīng)生命周期的方法,又可以把插件Activity的生命周期抽象成接口[8],在ProxyActivity的生命周期里調(diào)用。然后在插件Activity里使用R資源,因?yàn)閞es里的每一個(gè)資源都會(huì)在R.java里生成一個(gè)對(duì)應(yīng)的Integer類型的id,APP啟動(dòng)時(shí)會(huì)先把R.java注冊(cè)到當(dāng)前的上下文環(huán)境,在代碼里以R文件的方式使用資源時(shí)正是通過(guò)使用這些id訪問(wèn)res資源,然而插件的R.java并沒(méi)有注冊(cè)到當(dāng)前的上下文環(huán)境,所以插件的res資源也就無(wú)法通過(guò)id使用。想要解決此問(wèn)題,可以通過(guò)獲取一個(gè)AssetManager實(shí)例,使用其“addAssetPath”方法加載APK里的資源,再使用DisplayMetrics、Configuration、CompatibilityInfo實(shí)例一起創(chuàng)建所需要的Resources實(shí)例。訪問(wèn)插件APK里res資源的關(guān)鍵代碼如下:
3 結(jié)束語(yǔ)
本文分析了Android動(dòng)態(tài)加載的原理、過(guò)程以及實(shí)現(xiàn),解決了Android動(dòng)態(tài)加載中遇到的如何使插件APK里的Activity具有生命周期、如何使插件APK里的Activity具有上下文環(huán)境(使用R資源)等關(guān)鍵問(wèn)題。將此技術(shù)應(yīng)用到Android應(yīng)用中,可以解決Android應(yīng)用開(kāi)發(fā)到一定規(guī)模時(shí)APK安裝包過(guò)大,功能模塊過(guò)多,沒(méi)有辦法選擇性加載所需模塊的問(wèn)題,達(dá)到不安裝新APK就升級(jí)APP的目的,既可以顯著提高應(yīng)用新版本的覆蓋率,也可以減少服務(wù)器對(duì)舊版本接口兼容的壓力,同時(shí)也可以用來(lái)修復(fù)一些緊急BUG。
參考文獻(xiàn):
[1] David A. The Linux Documentation Project [EB/OL]. http://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html.
[2] 任玉剛. Android開(kāi)發(fā)藝術(shù)探索[M]. 北京: 電子工業(yè)出版社, 2015.
[3] Bill Phillips, Brian Hardy. Android programming: the big nerd ranch guide[M]. 北京: 人民郵電出版社, 2014.
[4] Meier R. Professional Android 4 Application Development[M]. 北京: 清華大學(xué)出版社, 2013.
[5] Hervé Guihot. Pro Android Apps Performance Optimization[M]. 北京: 人民郵電出版社, 2012.
[6] 林學(xué)森. 深入理解Android內(nèi)核設(shè)計(jì)思想[M]. 北京: 人民郵電出版社, 2014.
[7] Horstmann C S, Cornell G. Core Java V[M]. 北京: 人民郵電出版社, 2013.
[8] Bruce Eckel. Thinking In java[M]. 北京: 機(jī)械工業(yè)出版社, 2007.