Android 應用開發者應該對 UnsatisfiedLinkError 這種類型的錯誤比較熟悉了,這個問題一直困擾着廣大的開發者,那麼有沒有想過有可能你什麼都沒作錯,也會出現這個問題呢?java
咱們在 Android 應用開發測試過程當中曾經碰到過這樣的案例,apk 在某機型上安裝完成以後運行即崩潰,報錯 UnsatisfiedLinkError。android
java.lang.UnsatisfiedLinkError: Couldn’t load mobsec from loader dalvik.system.PathClassLoader.....findLibrary returned nullchrome
首先懷疑是在 apk 中相應的 libs\abi 目錄下沒有放置 libmobsec.so,然而檢查發現這個 so 在全部的 libs\abi 下都有放置過,繼續排查;shell
而後的想法是放置的 so 不是對應 abi 的,好比因爲粗心在 armeabi 目錄下放置了 x86 指令集的 so,致使在 armeabi 指令集手機上加載出錯,這個也被排除掉;app
就在沒有頭緒的時候,想到 System.loadLibrary 函數加載 so 時,系統是從指定的路徑下加載的,那麼這個路徑下 so 是否存在呢?函數
咱們知道應用的私有 Native library 目錄 /data/data/packagename/lib 是一個符號連接,連接到 /data/app-lib/<package name> 目錄,System.loadLibrary 是到這個目錄去嘗試加載 so 的。測試
adb shell 到這個路徑下,使用命令 ls 查看,果真這個 libmobsec.so 是不存在的。那麼是什麼緣由致使的呢?spa
分析 Android 系統源碼的實現,發現 /data/app-lib/<package name> 這個目錄下的 so ,是在系統安裝 apk 時從 apk 的 lib 目錄下去抽取的。.net
在安裝 app 時,Android package manager 代碼須要分析當前手機支持的指令集並拷貝相關指令集的 so。從 Android2.X 到 Android6.0 系統,因爲相繼加入了 x8六、64位等指令集的支持,這一部分代碼處理邏輯有很多變更,然而這個代碼是存在邏輯缺陷的,存在遺漏拷貝的可能,致使在一些機型上並不必定保證全部的 so 都能被正確抽取到 /data/app-lib/<package name> 目錄下,從而致使應用在加載 so 的時候出現 UnsatisfiedLinkError 這樣的錯誤。code
已經有開發者意識到這個 bug,好比在 Chromium 的源代碼的一段註釋,說明了 Android package manager 中的問題:
* PackageManager may fail to update shared library. * * Native library directory in an updated package is a symbolic link * to a directory in /data/app-lib/<package name>, for example: * /data/data/com.android.chrome/lib -> /data/app-lib/com.android.chrome[-1]. * When updating the application, the PackageManager create a new directory, * e.g., /data/app-lib/com.android.chrome-2, and remove the old symlink and * recreate one to the new directory. However, on some devices (e.g. Sony Xperia), * the symlink was updated, but fails to extract new native libraries from * the new apk.
「在 Android 平臺上加載本地庫的危險性」這篇文章中提到了做者遇到一樣的問題,並基於 Chromium 給出的一種權宜的解決辦法:封裝 System.loadLibrary 接口爲 ReLinker 接口,若是發現沒法正常加載 so,則獲取 apk 路徑並解壓相應指令集的 so,而後嘗試去加載。這種方案通過驗證是能夠顯著減小 UnsatisfiedLinkError 錯誤的出現,下圖爲做者使用了 ReLinker 接口後的日上報 UnsatisfiedLinkError 錯誤數的變化趨勢圖。
ReLinker 接口如今已經集成到網易雲捕的SDK中,使用方法以下:
用
ReLinker.loadLibrary(context, 「mylibrary」);
來代替
System.loadLibrary(「mylibrary」);
參考連接:
apk安裝過程及原理說明:http://blog.csdn.net/hdhd588/article/details/6739281
在Android平臺上加載本地庫的危險性:http://www.csdn.net/article/2015-11-10/2826182-the-perils-of-loading-native-libraries-on-android
更多資訊文章,可關注微博公衆號:網易雲捕