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