本文摘選自任玉剛著《Android開發藝術探索》,介紹了Android插件化技術的原理和三個關鍵問題,並給出了做者本身發起的開源插件化框架。git
動態加載技術(也叫插件化技術)在技術驅動型的公司中扮演着至關重要的角色,當項目愈來愈龐大的時候,須要經過插件化來減輕應用的內存和CPU佔 用,還能夠實現熱插拔,即在不發佈新版本的狀況下更新某些模塊。動態加載是一項很複雜的技術,這裏主要介紹動態加載技術中的三個基礎性問題,至於完整的動態加載技術的實現請參考筆者發起的開源插件化框架DL(https://github.com/singwhatiwanna/dynamic-load-apk)。項目期間有多位開發人員一塊兒貢獻代碼。github
不一樣的插件化方案各有各的特點,可是它們都必需要解決三個基礎性問題:資源訪問、Activity生命週期的管理和ClassLoader的管理。 在介紹它們以前,首先要明白宿主和插件的概念,宿主是指普通的apk,而插件通常是指通過處理的dex或者apk,在主流的插件化框架中多采用通過特殊處 理的apk來做爲插件,處理方式每每和編譯以及打包環節有關,另外不少插件化框架都須要用到代理Activity的概念,插件Activity的啓動大多 數是藉助一個代理Activity來實現的。框架
1.資源訪問性能
咱們知道,宿主程序調起未安裝的插件apk,一個很大的問題就是資源如何訪問,具體來講就是插件中凡是以R開頭的資源都不能訪問了。這是由於宿主程序中並無插件的資源,因此經過R來加載插件的資源是行不通的,程序會拋出異常:沒法找到某某id所對應的資源。學習
針對這個問題,有人提出了將插件中的資源在宿主程序中也預置一份,這雖然能解決問題,可是這樣就會產生一些弊端。首先,這樣就須要宿主和插件同時持 有一份相同的資源,增長了宿主apk的大小;其次,在這種模式下,每次發佈一個插件都須要將資源複製到宿主程序中,這意味着每發佈一個插件都要更新一下宿 主程序,這就和插件化的思想相違背了。大數據
由於插件化的目的就是要減少宿主程序apk包的大小,同時下降宿主程序的更新頻率並作到自由裝載模塊,因此這種方法不可取,它限制了插件的線上更新 這一重要特性。還有人提供了另外一種方式,首先將插件中的資源解壓出來,而後經過文件流去讀取資源,這樣作理論上是可行的,可是實際操做起來仍是有很大難度 的。首先不一樣資源有不一樣的文件流格式,好比圖片、XML等,其次針對不一樣設備加載的資源多是不同的,如何選擇合適的資源也是一個須要解決的問題,基於這兩點,這種方法也不建議使用,由於它實現起來有較大難度。爲了方便地對插件進行資源管理,下面給出一種合理的方式。spa
咱們知道,Activity的工做主要是經過ContextImpl來完成的, Activity中有一個叫mBase的成員變量,它的類型就是ContextImpl。注意到Context中有以下兩個抽象方法,看起來是和資源有關 的,實際上Context就是經過它們來獲取資源的。這兩個抽象方法的真正實如今ContextImpl中,也就是說,只要實現這兩個方法,就能夠解決資源問題了。插件
下面給出具體的實現方式,首先要加載apk中的資源,以下所示。代理
從loadResources()的實現能夠看出,加載資源的方法是經過反射,經過調用AssetManager中的addAssetPath方 法,咱們能夠將一個apk中的資源加載到Resources對象中,因爲addAssetPath是隱藏API咱們沒法直接調用,因此只能經過反射。下面 是它的聲明,經過註釋咱們能夠看出,傳遞的路徑能夠是zip文件也能夠是一個資源目錄,而apk就是一個zip,因此直接將apk的路徑傳給它,資源就加 載到AssetManager中了。而後再經過AssetManager來建立一個新的Resources對象,經過這個對象咱們就能夠訪問插件apk中 的資源了,這樣一來問題就解決了。orm
接着在代理Activity中實現getAssets()和getResources(),以下所示。關於代理Activity的含義請參看DL開源插件化框架的實現細節,這裏再也不詳細描述了。
經過上述這兩個步驟,就能夠經過R來訪問插件中的資源了。
2.Activity生命週期的管理
管理Activity生命週期的方式各類各樣,這裏只介紹兩種:反射方式和接口方式。反射的方式很好理解,首先經過Java的反射去獲取 Activity的各類生命週期方法,好比onCreate、onStart、onResume等,而後在代理Activity中去調用插件 Activity對應的生命週期方法便可,以下所示。
使用反射來管理插件Activity的生命週期是有缺點的,一方面是反射代碼寫起來比較複雜,另外一方面是過多使用反射會有必定的性能開銷。下面介紹 接口方式,接口方式很好地解決了反射方式的不足之處,這種方式將Activity的生命週期方法提取出來做爲一個接口(好比叫DLPlugin),而後通 過代理Activity去調用插件Activity的生命週期方法,這樣就完成了插件Activity的生命週期管理,而且沒有采用反射,這就解決了性能 問題。同時接口的聲明也比較簡單,下面是DLPlugin的聲明:
在代理Activity中只須要按以下方式便可調用插件Activity的生命週期方法,這就完成了插件Activity的生命週期的管理。
經過上述代碼應該不難理解接口方式對插件Activity生命週期的管理思想,其中mRemoteActivity就是DLPlugin的實現。
3.插件ClassLoader的管理
爲了更好地對多插件進行支持,須要合理地去管理各個插件的DexClassLoader,這樣同一個插件就能夠採用同一個ClassLoader去 加載類,從而避免了多個ClassLoader加載同一個類時所引起的類型轉換錯誤。在下面的代碼中,經過將不一樣插件的ClassLoader存儲在一個 HashMap中,這樣就能夠保證不一樣插件中的類彼此互不干擾。
事實上插件化的技術細節很是多,這絕非一個章節的內容所能描述清楚的,另外插件化做爲一種核心技術,須要開發者有較深的開發功底纔可以很好地理解, 所以本節的內容更可能是讓讀者對插件化開發有一個感性的瞭解,細節上還須要讀者本身去鑽研,也能夠經過DL插件化框架去深刻地學習。
阿里百川(baichuan.taobao.com)是阿里巴巴集團的無線開放平臺,經過「技術、商業及大數據」的開放,提供移動場景下的高內聚、開放式、行業領先的技術產品矩陣、成熟的商業組件和完善的服務體系,幫助移動開發者快速搭建APP、加速APP商業化進程,全方位賦能移動開發者及移動創業者。