最近幾年移動開發業界興起了「 插件化技術 」的旋風,各個大廠都推出了本身的插件化框架,各類開源框架都評價自身功能優越性,使人應接不暇。隨着公司業務快速發展,項目增多,開發資源卻有限,如何能在有限資源內知足需求和項目的增加,同時又能快速響應問題和迭代新需求,這就是一個矛盾點。此時,插件化技術正好風生水起,去了解各個主流框架實現思路,看看能對目前工做是否有幫助,是頗有必要的。html
主要分爲如下幾個部分android
百度百科裏是這麼定義插件的:「 是一種遵循必定規範的應用程序接口編寫出來的程序,只能運行在程序規定的系統平臺下,而不能脫離指定的平臺單獨運行。」,也就是說,插件能夠提供一種動態擴展能力,使得應用程序在運行時加載本來不屬於該應用的功能,而且作到動態更新和替換。git
那麼在 Android 中,何爲「 插件化 」,顧名思義,就是把一些核心複雜依賴度高的業務模塊封裝成獨立的插件,而後根據不一樣業務需求進行不一樣組合,動態進行替換,可對插件進行管理、更新,後期對插件也可進行版本管理等操做。在插件化中有兩個概念須要講解下:github
宿主編程
所謂宿主,就是須要能提供運行環境,給資源調用提供上下文環境,通常也就是咱們主 APK ,要運行的應用,它做爲應用的主工程所在,實現了一套插件的加載和管理的框架,插件都是依託於宿主的APK而存在的。安全
插件性能優化
插件能夠想象成每一個獨立的功能模塊封裝爲一個小的 APK ,能夠經過在線配置和更新實現插件 APK 在宿主 APK 中的上線和下線,以及動態更新等功能。服務器
那麼爲什麼要使用插件化技術,它有何優點,能給咱們帶來什麼樣好處,這裏簡單列舉了如下幾點:網絡
首先咱們要知道插件化技術是屬於比較複雜一個領域,複雜點在於它涉及知識點普遍,不只僅是上層作應用架構能力,還要求咱們對 Android 系統底層知識須要有必定的認知,這裏簡單羅列了其中會涉及的知識點:架構
首先,要介紹的是 Binder ,咱們都知道 Android 多進程通訊核心就是 Binder ,若是沒有它真的步履維艱。 Binder 涉及兩層技術,你能夠認爲它是一箇中介者模式,在客戶端和服務器端之間, Binder 就起到中介的做用。若是要實現四大組件的插件化,就須要在 Binder 上作修改, Binder 服務端的內容沒辦法修改,只能改客戶端的代碼,並且四大組件的每一個組件的客戶端都不同,這個就須要深刻研究了。學習Binder的最好方式是 AIDL ,這方面在網上有不少資料,最簡單的方式就是本身寫個 aidl 文件自動生成一個 Java 類,而後去查看這個Java類的每一個方法和變量,而後再去看四大組件,其實都是跟 AIDL 差很少的實現方式。
其次,是 App 打包的流程。代碼寫完了,執行一次打包操做,中途經歷了資源打包、 Dex 生成、簽名等過程。其中最重要的就是資源的打包,即 AAPT 這一步,若是宿主和插件的資源id衝突,一種解決辦法就是在這裏作修改。
第三, App 在手機上的安裝流程也很重要。熟悉安裝流程不只對插件化有幫助,在遇到安裝 Bug 的時候也很是重要。手機安裝 App 的時候,常常會有下載異常,提示資源包不能解析,這時須要知道安裝 App 的這段代碼在什麼地方,這只是第一步。第二步須要知道, App 下載到本地後,具體要作哪些事情。手機有些目錄不能訪問, App 下載到本地以後,放到哪一個目錄下,而後會生成哪些文件。插件化有個增量更新的概念,如何下載一個增量包,從本地具體哪一個位置取出一個包,這個包的具體命名規則是什麼,等等。這些細節都必需要清楚明白。
第四,是 App 的啓動流程。 Activity 啓動有幾種方式?一種是寫一個 startActivity ,第二種是點擊手機 App ,經過手機系統裏的 Launcher 機制,啓動 App 裏默認的 Activity 。一般, App 開發人員喜聞樂見的方式是第二種。那麼第一種方式的啓動原理是什麼呢?另外,啓動的時候,Main 函數在哪裏?這個 Main 函數的位置很重要,咱們能夠對它所在的類作修改,從而實現插件化。
第五點更重要,作 Android 插件化須要控制兩個地方。首先是插件 Dex 的加載,如何把插件 Dex 中的類加載到內存?另外是資源加載的問題。插件多是 Apk 也多是 so 格式,無論哪種,都不會生成 R.id ,從而沒辦法使用。這個問題有好幾種解決方案。一種是是重寫 Context 的 getAsset 、 getResource 之類的方法,偷換概念,讓插件讀取插件裏的資源,但缺點就是宿主和插件的資源 id 會衝突,須要重寫 AAPT 。另外一種是重寫 AMS中保存的插件列表,從而讓宿主和插件分別去加載各自的資源而不會衝突。第三種方法,就是打包後,執行一個腳本,修改生成包中資源id。
第六點,在實施插件化後,如何解決不一樣插件的開發人員的工做區問題。好比,插件1和插件2,須要分別下載哪些代碼,如何獨立運行?就像機票和火車票,如何只運行本身的插件,而不運行別人的插件?這是協同工做的問題。火車票和機票,這兩個 Android 團隊的各自工做區是不同的,這時候就要用到 Gradle 腳本了,每一個項目分別有各自的倉庫,有各自不一樣的打包腳本,只須要把本身的插件跟宿主項目一塊兒打包運行起來,而不用引入其餘插件,還有更厲害的是,也能夠把本身的插件看成一個 App 來打包並運行。
上面介紹了插件化的入門知識,一共六點,每一點都須要花大量時間去理解。不然,在面對插件化項目的時候,不少地方你會一頭霧水。而只要理解了這六點核心,一切可迎刃而解。
在Android中應用插件化技術,其實也就是動態加載的過程,分爲如下幾步:
Android 項目中,動態加載技術按照加載的可執行文件的不一樣大體能夠分爲兩種:
第一點, Android 中 NDK 中其實就使用了動態加載,動態加載 .so 庫並經過 JNI 調用其封裝好的方法。後者通常是由 C/C++ 編譯而成,運行在 Native 層,效率會比執行在虛擬機層的 Java 代碼高不少,因此 Android 中常常經過動態加載 .so 庫來完成一些對性能比較有需求的工做(好比 Bitmap 的解碼、圖片高斯模糊處理等)。此外,因爲 .so 庫是由 C/C++ 編譯而來的,只能被反編譯成彙編代碼,相比中 dex 文件反編譯獲得的 Smali 代碼更難被破解,所以 .so 庫也能夠被用於安全領域。
其二,「基於 ClassLoader 的動態加載 dex/jar/apk 文件」,就是咱們指在 Android 中 動態加載由 Java 代碼編譯而來的 dex 包並執行其中的代碼邏輯,這是常規 Android 開發比較少用到的一種技術,目前說的動態加載指的就是這種。
Android 項目中,全部 Java 代碼都會被編譯成 dex 文件,Android 應用運行時,就是經過執行 dex 文件裏的業務代碼邏輯來工做的。使用動態加載技術能夠在 Android 應用運行時加載外部的 dex 文件,而經過網絡下載新的 dex 文件並替換原有的 dex 文件就能夠達到不安裝新 APK 文件就升級應用(改變代碼邏輯)的目的。
因此說,在 Android 中的 ClassLoader 機制主要用來加載 dex 文件,系統提供了兩個 API 可供選擇:
在 Android 中實現插件化框架,須要解決的問題主要以下:
下面分析幾個目前主流的開源框架,看看每一個框架具體實現思路和優缺點。
DL 動態加載框架 ( 2014 年末)
是基於代理的方式實現插件框架,對 App 的表層作了處理,經過在 Manifest 中註冊代理組件,當啓動插件組件時,首先啓動一個代理組件,而後經過這個代理組件來構建,啓動插件組件。 須要按照必定的規則來開發插件 APK,插件中的組件須要實現通過改造後的 Activity、FragmentActivity、Service 等的子類。
優勢以下:
缺點以下:
DroidPlugin ( 2015 年 8 月)
DroidPlugin 是 360 手機助手實現的一種插件化框架,它能夠直接運行第三方的獨立 APK 文件,徹底不須要對 APK 進行修改或安裝。一種新的插件機制,一種免安裝的運行機制,是一個沙箱(可是不徹底的沙箱。就是對於使用者來講,並不知道他會把 apk 怎麼樣), 是模塊化的基礎。
實現原理:
插件 Host 的程序架構:
優勢以下:
缺點以下:
Small ( 2015 年末)
Small 是一種實現輕巧的跨平臺插件化框架,基於「輕量、透明、極小化、跨平臺」的理念,實現原理有如下三點。
架構圖:
優勢以下:
缺點以下:
與其餘主流框架的區別:
DyLA : Dynamic-load-apk @singwhatiwanna DiLA : Direct-Load-apk @FinalLody APF : Android-Plugin-Framework @limpoxe ACDD : ACDD @bunnyblue DyAPK : DynamicAPK @TediWang DPG : DroidPlugin @cmzy, 360
DyLA | DiLA | ACDD | DyAPK | DPG | APF | Small | |
---|---|---|---|---|---|---|---|
加載非獨立插件 | × | x | √ | √ | × | √ | √ |
加載.so後綴插件 | × | × | ! | × | × | × | √ |
Activity生命週期 | √ | √ | √ | √ | √ | √ | √ |
Service動態註冊 | × | × | √ | × | √ | √ | x |
資源分包共享 | × | × | ! | ! | × | ! | √ |
公共插件打包共享 | × | × | × | × | × | × | √ |
支持AppCompat | × | × | × | × | × | × | √ |
支持本地網頁組件 | × | × | × | × | × | × | √ |
支持聯調插件 | × | x | × | × | × | × | √ |
ACDD | DyAPK | APF | Small | |
---|---|---|---|---|
插件Activity代碼無需修改 | √ | √ | √ | √ |
插件引用外部資源無需修改name | × | × | × | √ |
插件模塊無需修改build.gradle | × | x | × | √ |
VirtualAPK (2017年 6 月 )
VirtualAPK 是滴滴開源的一套插件化框架,支持幾乎全部的 Android 特性,四大組件方面。
架構圖:
實現思路:
VirtualAPK 對插件沒有額外的約束,原生的 apk 便可做爲插件。插件工程編譯生成 apk後,便可經過宿主 App 加載,每一個插件 apk 被加載後,都會在宿主中建立一個單獨的 LoadedPlugin 對象。以下圖所示,經過這些 LoadedPlugin 對象,VirtualAPK 就能夠管理插件並賦予插件新的意義,使其能夠像手機中安裝過的 App 同樣運行。
特性以下:
四大組件均不須要在宿主manifest中預註冊,每一個組件都有完整的生命週期。
theme
和LaunchMode
,支持透明主題;start
、stop
、bind
和unbind
,並支持跨進程bind插件中的Service;CRUD
和call
方法等,支持跨進程訪問插件中的Provider。自定義 View
,支持自定義屬性和style
,支持動畫;PendingIntent
以及和其相關的Alarm
、Notification
和AppWidget
;Application
以及插件manifest中的meta-data
;so
。優秀的兼容性
AMS
和IContentProvider
,hook 過程作了充分的兼容性適配。入侵性極低
以下是 VirtualAPK 和主流的插件化框架之間的對比。
特性 | DynamicLoadApk | DynamicAPK | Small | DroidPlugin | VirtualAPK |
---|---|---|---|---|---|
支持四大組件 | 只支持Activity | 只支持Activity | 只支持Activity | 全支持 | 全支持 |
組件無需在宿主manifest中預註冊 | √ | × | √ | √ | √ |
插件能夠依賴宿主 | √ | √ | √ | × | √ |
支持 PendingIntent | × | × | × | √ | √ |
Android 特性支持 | 大部分 | 大部分 | 大部分 | 幾乎所有 | 幾乎所有 |
兼容性適配 | 通常 | 通常 | 中等 | 高 | 高 |
插件構建 | 無 | 部署aapt | Gradle插件 | 無 | Gradle插件 |
RePlugin (2017 年 7 月)
RePlugin是一套完整的、穩定的、適合全面使用的,佔坑類插件化方案,由360手機衛士的RePlugin Team研發,也是業內首個提出」全面插件化「(全面特性、全面兼容、全面使用)的方案。
框架圖:
主要優點有:
極其靈活:主程序無需升級(無需在Manifest中預埋組件),便可支持新增的四大組件,甚至全新的插件
很是穩定:Hook 點僅有一處(ClassLoader),無任何 Binder Hook!如此可作到其崩潰率僅爲「萬分之一」,並完美兼容市面上近乎全部的 Android ROM。
特性豐富:支持近乎全部在「單品」開發時的特性。包括靜態 Receiver、 Task-Affinity 坑位、自定義 Theme、進程坑位、AppCompat、DataBinding等。
易於集成:不管插件仍是主程序,只需「數行」就能完成接入。
管理成熟:擁有成熟穩定的「插件管理方案」,支持插件安裝、升級、卸載、版本管理,甚至包括進程通信、協議版本、安全校驗等。
數億支撐:有 360 手機衛士龐大的數億用戶作支撐,三年多的殘酷驗證,確保App用到的方案是最穩定、最適合使用的。
主要是測試各個框架之間上手的容易度如何,並作不一樣對比,這邊寫了兩個 Demo 例子,一個是基於 Small 框架,一個基於 VirtualAPK 框架,從中能看出不一樣。
Small 實踐
要引用官方最新的版本,否則在宿主和插件合併build.gradle
的時候會出現一個 BUG,這是個坑位,注意行走。其次在模塊命名上要遵循必定的規則,好比業務模塊用 app.* ,公共庫模塊用 lib.* ,至關於包名 .app.,.lib. 。每次在插件中添加一個 activity 組件,都須要在宿主中配置路由,而後在從新編譯插件一遍,否則直接運行的話,在宿主中是找到新添加的 activity 組件,會報該組件沒在系統 manifest 中,因此每次新增或修改建議插件都從新編譯一遍。官方里說了,對於 Service 支持不太友好,就沒去實踐了。
VirtualAPK 實踐
有個坑須要注意的是構建環境,官方說明是要如下版本環境,Gradle 2.14.1 和 com.android.tools.build 2.1.3, 以前編譯的是用最新的Gradle版本,致使一直有問題,至因而否有其餘問題,能夠看官方文檔。
具體代碼
Small Demo :https://github.com/cr330326/MySmall
VirtualAPK Demo :https://github.com/cr330326/MyVirtualAPKDemo
正如開頭所說,要實現插件化的框架,無非就是解決那典型的三個問題:插件代碼如何加載、插件中的組件生命週期如何管理、插件資源和宿主資源衝突怎麼辦。每一個框架針對這三個問題,都有不一樣的解決方案,同時呢,根據時間順序,後出來的框架每每都會吸取已經出的框架精髓,進而修復那些比較有里程碑意義框架的不足。但這些框架的核心思想都是用到了代理模式,有的在表面層進行代理,有的則在系統應用層進行代理,經過代理達到替換和瞞天過海,最終讓 Android 系統誤覺得調用插件功能和調用原生開發的功能是同樣的,進而達到插件化和原生兼容編程的目的。
2,包建強的無線技術空間,寫給Android App 開發人員看的 Android 底層知識 置頂8篇
源於對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中能夠看到技術積累的過程。
1,Android系統簡介
2,ProGuard代碼混淆
3,講講Handler+Looper+MessageQueue關係
4,Android圖片加載庫理解
5,談談Android運行時權限理解
6,EventBus初理解
7,Android 常見工具類
8,對於Fragment的一些理解
9,Android 四大組件之 " Activity "
10,Android 四大組件之" Service "
11,Android 四大組件之「 BroadcastReceiver "
12,Android 四大組件之" ContentProvider "
13,講講 Android 事件攔截機制
14,Android 動畫的理解
15,Android 生命週期和啓動模式
16,Android IPC 機制
17,View 的事件體系
18,View 的工做原理
19,理解 Window 和 WindowManager
20,Activity 啓動過程分析
21,Service 啓動過程分析
22,Android 性能優化
23,Android 消息機制
24,Android Bitmap相關
25,Android 線程和線程池
26,Android 中的 Drawable 和動畫
27,RecylerView 中的裝飾者模式
28,Android 觸摸事件機制
29,Android 事件機制應用
30,Cordova 框架的一些理解
31,有關 Android 插件化思考
32,開發人員必備技能——單元測試