Android插件化思考幸運飛艇平臺出租最新最全總結
百度百科裏是幸運飛艇平臺出租(www.1159880099.com) QQ1159880099這麽定義插件的:「 是一種遵循一定規範的應用程序接口編寫出來的程序,只能運行在程序規定的系統平臺下,而不能脫離指定的平臺單獨運行。」,也就是說,插件可以提供一種動態擴展能力,使得應用程序在運行時加載原本不屬於該應用的功能,並且做到動態更新和替換。
那麽在 Android 中,何為「 插件化 」,顧名思義,就是把一些核心復雜依賴度高的業務模塊封裝成獨立的插件,然後根據不同業務需求進行不同組合,動態進行替換,可對插件進行管理、更新,後期對插件也可進行版本管理等操作。在插件化中有兩個概念需要講解下:
宿主
所謂宿主,就是需要能提供運行環境,給資源調用提供上下文環境,一般也就是我們主 APK ,要運行的應用,它作為應用的主工程所在,實現了一套插件的加載和管理的框架,插件都是依托於宿主的APK而存在的。
插件
插件可以想象成每個獨立的功能模塊封裝為一個小的 APK ,可以通過在線配置和更新實現插件 APK 在宿主 APK 中的上線和下線,以及動態更新等功能。
那麽為何要使用插件化技術,它有何優勢,能給我們帶來什麽樣好處,這裏簡單列舉了以下幾點:
讓用戶不用重新安裝 APK 就能升級應用功能,減少發版本頻率,增加用戶體驗。
提供一種快速修復線上 BUG 和更新的能力。
按需加載不同的模塊,實現靈活的功能配置,減少服務器對舊版本接口兼容壓力。
模塊化、解耦合、並行開發、 65535 問題。
入門知識
首先我們要知道插件化技術是屬於比較復雜一個領域,復雜點在於它涉及知識點廣泛,不僅僅是上層做應用架構能力,還要求我們對 Android 系統底層知識需要有一定的認知,這裏簡單羅列了其中會涉及的知識點
640?wx_fmt=png
首先,要介紹的是 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中應用插件化技術,其實也就是動態加載的過程,分為以下幾步:
把可執行文件( .so/dex/jar/apk 等)拷貝到應用 APP 內部。
加載可執行文件,更換靜態資源
調用具體的方法執行業務邏輯
Android 項目中,動態加載技術按照加載的可執行文件的不同大致可以分為兩種:
動態加載 .so 庫
動態加載 dex/jar/apk文件(現在動態加載普遍說的是這種)
第一點, 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 可供選擇:
PathClassLoader:只能加載已經安裝到 Android 系統中的 APK 文件。因此不符合插件化的需求,不作考慮。
DexClassLoader:支持加載外部的 APK、Jar 或者 dex 文件,正好符合文件化的需求,所有的插件化方案都是使用 DexClassloader 來加載插件 APK 中的 .class文件的。
主流框架
在 Android 中實現插件化框架,需要解決的問題主要如下:
資源和代碼的加載
Android 生命周期的管理和組件的註冊
宿主 APK 和插件 APK 資源引用的沖突解決
下面分析幾個目前主流的開源框架,看看每個框架具體實現思路和優缺點。
DL 動態加載框架 ( 2014 年底)
是基於代理的方式實現插件框架,對 App 的表層做了處理,通過在 Manifest 中註冊代理組件,當啟動插件組件時,首先啟動一個代理組件,然後通過這個代理組件來構建,啟動插件組件。 需要按照一定的規則來開發插件 APK,插件中的組件需要實現經過改造後的 Activity、FragmentActivity、Service 等的子類。
優點如下:
插件需要遵循一定的規則,因此安全方面可控制。
方案簡單,適用於自身少量代碼的插件化改造。
缺點如下:
不支持通過 This 調用組件的方法,需要通過 that 去調用。
由於 APK 中的 Activity 沒有註冊,不支持隱式調用 APK 內部的 Activity。
插件編寫和改造過程中,需要考慮兼容性問題比較多,聯調起來會比較費時費力。
DroidPlugin ( 2015 年 8 月)
DroidPlugin 是 360 手機助手實現的一種插件化框架,它可以直接運行第三方的獨立 APK 文件,完全不需要對 APK 進行修改或安裝。一種新的插件機制,一種免安裝的運行機制,是一個沙箱(但是不完全的沙箱。就是對於使用者來說,並不知道他會把 apk 怎麽樣), 是模塊化的基礎。
實現原理:
共享進程:為android提供一個進程運行多個 apk 的機制,通過 API 欺騙機制瞞過系統。
占坑:通過預先占坑的方式實現不用在 manifest 註冊,通過一帶多的方式實現服務管理。
Hook 機制:動態代理實現函數 hook ,Binder 代理繞過部分系統服務限制,IO 重定向(先獲取原始 Object –> Read ,然後動態代理 Hook Object 後–> Write 回去,達到瞞天過海的目的)。
插件 Host 的程序架構:
640?wx_fmt=png
優點如下:
支持 Android 四大組件,而且插件中的組件不需要在宿主 APK 中註冊。
支持 Android 2.3 及以上系統,支持所有的系統 API。
插件與插件之間,插件與宿主之間的代碼和資源完全隔閡。
實現了進程管理,插件的空進程會被及時回收,占用內存低。
缺點如下:
插件 APK 中不支持自定義資源的 Notification,通知欄限制。
插件 APK 中無法註冊具有特殊的 IntentFilter 的四大組件。
缺乏對 Native 層的 Hook 操作,對於某些帶有 Native 代碼的插件 APK 支持不友好,可能無法正常運行。
由於插件與插件,插件與宿主之間的代碼完全隔離,因此,插件與插件,插件與宿主之間的通信只能通過 Android 系統級別的通信方式。
安全性擔憂(可以修改,hook一些重要信息)。
機型適配(不是所有機器上都能行,因為大量用反射相關,如果rom廠商深度定制了framework層,反射的方法或者類不在,容易插件運用失敗)
Small ( 2015 年底)
Small 是一種實現輕巧的跨平臺插件化框架,基於“輕量、透明、極小化、跨平臺”的理念,實現原理有以下三點。
動態加載類:我們知道插件化很多都從 DexClassLoader 類有個 DexPathList 清單,支持 dex/jar/zip/apk 文件格式,卻沒有支持 .so 文件格式,因此 Small 框架則是把 .so 文件包裝成 zip 文件格式,插入到 DexPathList 集合中,改寫動態加載的代碼。
資源分段:由於 Android 資源的格式是 0xPPTTNNNN ,PP 是包 ID ,00-02 是屬於系統,7f 屬於應用程序,03-7e 則保留,可以在這個範圍內做文章 , TT 則是 Type 比如,attr 、layout 、string 等等,NNNN 則是資源全局 ID。那麽這個框架則是對資源包進行重新打包,每個插件重新分配資源 ID ,這樣就保證了宿主和插件的資源不沖突。
動態代理註冊:在 Android 中要使用四大組件,都是需要在 manifest 清單中註冊,這樣才可以使用,那如何在不註冊情況也能使用呢,這裏就是用到動態代理機制進行 Hook ,在發送 AMS 之前用占坑的組件來欺騙系統,通過認證後,再把真正要調用的組件還原回來,達到瞞天過海目的。
Android插件化思考幸運飛艇平臺出租最新最全總結