Android解耦式so庫加載方案

背景說明

在業務開發過程當中常常會進入一些三方sdk,這些三方的sdk引入so庫,有些so庫文件還比較大,這時候咱們就須要考慮so庫從網絡獲取異步加載,減小發布包的體積java

image.png

傳統方案

關於so異步加載方案,網上的資料隨便搜下大把,核心思想
so庫文件放到網絡->下載到本地沙盒->經過System.load載入
看起來挺簡單的 然而挺多資料沒提到的是android

so庫存在依賴關係

好比當你把libunity.so下載下來經過
System.load("/data/data/com.example.soload/files/libunity.so")
加載的時候會獲得以下異常git

java.lang.UnsatisfiedLinkError: dlopen failed: library 「libmain.so」 not found
at java.lang.Runtime.loadLibrary0(Runtime.java:1071)
at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
at java.lang.System.loadLibrary(System.java:1667)

緣由分析github

image.png

解決方案編程

載入libunity.so以前須要先載入libmain.sojson

擴展思考api

咱們寫代碼的時候怎麼控制加載順序呢
https://github.com/facebook/S...
https://github.com/KeepSafe/R...
以上2個開源庫 都有處理相關邏輯
核心思想就是解析so庫ELF格式,分析依賴並遞歸,直到依賴庫都加載完成再載入自身
這裏不擴展開來 有興趣的夥伴能夠看源碼研究下網絡

text relocations問題

當你把liblbs.so下載下來經過app

System.load("/data/data/com.example.soload/files/liblbs.so")的時候又出現意外了
java.lang.UnsatisfiedLinkError: dlopen failed: 「/data/data/com.example.soload/files/liblbs.so」 has text relocations ( https://android.googlesource....
at java.lang.Runtime.load0(Runtime.java:938)
at java.lang.System.load(System.java:1631)==

緣由分析:異步

https://blog.csdn.net/chjqxxx...

擴展思考:

咱們能夠經過命令進行自查

image.png

有源碼的能夠從新編譯,沒有源碼的只能經過下降targetSdkVersion處理(然而也是治標不治本)

進階方案

除了System.load方案 還有沒有其餘方案呢
答案固然是確定的
https://github.com/Tencent/ti...
核心思路:經過反射把自定義的native庫path插入nativeLibraryDirect ories最前面,即便安裝包libs目錄裏面有同名的so,也優先加載指定路徑的外部so
這裏截取tinker部分代碼
com.tencent.tinker.lib.library.TinkerLoadLibrary.java

image.png

image.png

這個方案是否是看起來更簡單了,因爲這個方案是很是規手段,存在兼容風險, 咱們須要驗證該方案的兼容性,寫個helloword的so,經過該方案加載,提早找個版本線上帶上去,進行埋點統計
截至目前最新版上線2天 統計到數據以下

image.png

其中失敗的1例是上線前自測故意讓校驗失敗產生的
也就是說截至目前能夠認爲改方案是靠譜的
固然了若是後續有新的Android系統版本更新
咱們還須要關注新版本的兼容狀況

項目應用

上面鋪墊了那麼多,如今進入正題
技術方案是一回事,落實到實際項目是另外一回事
如今開始咱們思考下以下幾個問題

  1. 項目中引用的so庫都是那些功能再用什麼時候載入
  2. 三方jar(aar)引入的so庫加載時機不受咱們代碼邏輯控制
  3. 使用的so庫那些適合作異步加載
  4. so庫文件沒有下載完成以前 用戶用相關功能如何處理
  5. 版本迭代so文件升級如何處理
  6. 打包階段如何把so文件進行剔除

解決問題一、二、3

咱們能夠經過切面編程的思路解決,這裏用到了開源庫https://github.com/HujiangTec...
因爲so庫的加載都是經過調用系統函數System.loadLibrary進行,
那麼咱們全局攔截該函數的調用並打印調用鏈,
運行app,配合日誌,就能分析so庫的具體使用狀況

image.png
image.png

這裏咱們須要根據本身業務場景分析,好比app進入到首頁依賴的庫不建議進行異步加載,須要異步加載的庫最好是完成一個功能(咱們須要按照經驗進行邏輯分組 參考文章開頭的圖)

解決問題四、5

Android是依據Activity做爲活動單位,啓動Activity是經過startActivity函數調用進行的,那麼咱們就能夠攔截織入本身的邏輯
大體思路以下
image.png

對應實現部分代碼截圖
image.png

image.png

image.png
image.png

經過規則配置化+字節碼攔截的邏輯 對原有業務無侵入便可實現動態加載
研發關注正常的業務邏輯 不須要針對性編寫代碼
後續引入新的so庫,修改配置文件便可
在實現的過程當中有些細節是須要考慮的
好比校驗下載文件的完整性,下載要不要支持斷點續傳,so文件須要更新如何處理等

解決問題6

經過上面的介紹,咱們實現了so文件的異步加載
可是打包的時候 咱們如何剔除so 減小最終發佈包的大小
能夠在build.gradle進行配置
image.png

可是上面提到了咱們是經過json配置的規則
這樣再重複配置一遍
並且還有可能2處配置不一致
並且咱們的大原則是解耦
那麼咱們就繼續hook打包流程
image.png

image.png

彙總小結

經過上面的介紹大體說明了原理

有興趣的夥伴能夠評論區留言,提出本身的想法

目前還在灰度驗證階段,等線上驗證穩定後在評論區放出相關代碼

so異步加載方案大同小異,該方案跟其餘方案相比

我的感受特點就是侵入性低

接入只須要3個步驟
image.png
image.png
image.png

未完待續

目前該方案還存在幾個點有待優化

====================

問題1

目前用的是進階方案實現的so庫加載 比較理想的方式是傳統方案和進階方案都應該支持 一開始其實我想用傳統方案去實現 可是該方案在unity那塊有問題(unity相關so的加載邏輯有點特殊 存在經過jni直接加載so的狀況 因爲沒有源碼 內部邏輯不太清楚 ) 後續還須要研究完善 畢竟官方System.load是官方提供的api 更可靠

問題2

進度加載的dialog是依附於調用startActivity的窗口
若是startActivuty以後裏面 立馬調用finish會致使dialog泄露
這個是須要注意的一個點 也就意味着對原有邏輯存在必定依賴
這個能夠經過啓動一個FLAG_ACTIVITY_NEW_TASK類型的activity
代理解決 可是感受這樣有點重 目前還有點糾結

問題3

目前文件只是放到了阿里雲,用戶的網絡環境複雜多變,須要考慮支持騰訊雲,七牛雲等更多的服務商。

問題4

上傳到oss的動做目前是動手完成的,須要作成腳本。

相關文章
相關標籤/搜索