背景說明java
在業務開發過程當中常常會進入一些三方sdk,這些三方的sdk引入so庫,有些so庫文件還比較大,這時候咱們就須要考慮so庫從網絡獲取異步加載,減小發布包的體積編程
傳統方案segmentfault
關於so異步加載方案,網上的資料隨便搜下大把,核心思想
so庫文件放到網絡->下載到本地沙盒->經過System.load載入
看起來挺簡單的 然而挺多資料沒提到的是網絡
so庫存在依賴關係app
好比當你把友鏈交易libunity.so下載下來經過
System.load("/data/data/com.example.soload/files/libunity.so")
加載的時候會獲得以下異常異步
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)函數
解決方案url
載入libunity.so以前須要先載入libmain.so日誌
以上2個開源庫 都有處理相關邏輯
核心思想就是解析so庫ELF格式,分析依賴並遞歸,直到依賴庫都加載完成再載入自身
這裏不擴展開來 有興趣的夥伴能夠看源碼研究下遞歸
text relocations問題
當你把liblbs.so下載下來經過
System.load("/data/data/com.example.soload/files/liblbs.so")的時候又出現意外了
java.lang.UnsatisfiedLinkError: dlopen failed: 「/data/data/com.example.soload/files/liblbs.so」 has
at java.lang.Runtime.load0(Runtime.java:938)
at java.lang.System.load(System.java:1631)==
擴展思考:
咱們能夠經過命令進行自查
有源碼的能夠從新編譯,沒有源碼的只能經過下降targetSdkVersion處理(然而也是治標不治本)
進階方案
除了System.load方案 還有沒有其餘方案呢
答案固然是確定的
核心思路:經過反射把自定義的native庫path插入nativeLibraryDirect ories最前面,即便安裝包libs目錄裏面有同名的so,也優先加載指定路徑的外部so
這裏截取tinker部分代碼
com.tencent.tinker.lib.library.TinkerLoadLibrary.java
這個方案是否是看起來更簡單了,因爲這個方案是很是規手段,存在兼容風險, 咱們須要驗證該方案的兼容性,寫個helloword的so,經過該方案加載,提早找個版本線上帶上去,進行埋點統計
截至目前最新版上線2天 統計到數據以下
其中失敗的1例是上線前自測故意讓校驗失敗產生的
也就是說截至目前能夠認爲改方案是靠譜的
固然了若是後續有新的Android系統版本更新
咱們還須要關注新版本的兼容狀況
項目應用
上面鋪墊了那麼多,如今進入正題
技術方案是一回事,落實到實際項目是另外一回事
如今開始咱們思考下以下幾個問題
項目中引用的so庫都是那些功能再用什麼時候載入
三方jar(aar)引入的so庫加載時機不受咱們代碼邏輯控制
使用的so庫那些適合作異步加載
so庫文件沒有下載完成以前 用戶用相關功能如何處理
版本迭代so文件升級如何處理
打包階段如何把so文件進行剔除
解決問題一、二、3
咱們能夠經過切面編程的思路解決,這裏用到了開源因爲so庫的加載都是經過調用系統函數System.loadLibrary進行,那麼咱們全局攔截該函數的調用並打印調用鏈,運行app,配合日誌,就能分析so庫的具體使用狀況