Android 4.X 系統加載 so 失敗的原因分析
1 so 加載過程
so 加載的過程可以參考小米的系統工程師的文章loadLibrary動態庫加載過程分析
2 問題分析
2.1 問題
年前項目裏新加了一個 so庫,但發現native 方法的找不到的 crash
好多,好些都是報了java.lang.unsatisfiedlinkerror native method not found
,而且基本上是出現在4.x的系統裏,特別是 4.4,4.2的系統。在網絡上搜索相關的可能導致到這個問題的原因:
- so 文件沒有在對應架構的目錄裏找到;
方法名有錯誤;
2.2 分析1
我們最開始是懷疑應用在安裝時沒有正確解壓出對應的
so
文件到相應目錄,因此加了相應統計來看發生crash
so
文件導致的;但統計數據發現這些手機裏都可以找到對應架構的so
文件,因此就排除了不存在so
文件導致的crash
;
2.3 分析2
我們同事以前有發現在Android 4.x
系統裏,如果so
文件是在應用啟動時加載的,但使用時機卻在後面的時間點,so
加載進手機的內存可能會被系統由於資源緊張而回收掉,這種情況下,可以通過重新加載一次 so
文件來減少相關的 crash
,這種方法 fix
了某個量很高的 so 相關的crash
。但我們的 so
的 crash
明顯是不屬於這種情況的,因為我們是通過 System.load()
方法加載完 so
文件後,就調用相關的方法,這時內存肯定是還在的。在分析了一系列可能的原因後,懷疑這個crash
so
文件是損壞的,因此我們嘗試在第一次發生這個crash
時,將這個crash
catch
住,然後在 catch
塊將原來目錄下的 so
文件刪除掉,並重新從應用的安裝目錄解壓出對應的so
文件放到原來的目錄,並加了相關的統計來驗證。so
的加載用了 Relinkder。相關的簡化版本代碼如下:
relinker.loadLibrary(getApplicationContext(), "so_name");
try{
// call native method
} catch(UnsatisfiedLinkError e) {
//some stats
String library = "so_name" ;
String libName = System.mapLibraryName(library);
File workaroundLibDir = getApplicationContext().getDir("lib", Context.MODE_PRIVATE);
File workaroundLibFile = new File(workaroundLibDir, libName);
workaroundLibFile.delete();
apkLibraryInstaller.installLibrary(
getApplicationContext()
, supportedAbis()
, libName
, workaroundLibFile
, relinker
);
System.load(workaroundLibFile.getAbsolutePath());
//call native method
// some stats
}
2.4 分析3
在使用的 2.3 的解決方法後,我們的 so
的 java.lang.unsatisfiedlinkerror native method not found
大部分消失了。理論上使用過一次重新解壓so
文件後,這個用戶在下一次升級前都應該不會再發生了類似的 crash
了,但我們的統計數據發現,有些用戶每一次啟動都需要進入catch
塊來避免crash
,而每次都可以通過 reload
來正常使用我們的應用,這至今還是個迷,還沒有想明白是什麽情況會導致這個問題?手機的存儲有問題?但其他的so
又沒有這個問題。希望如果有同行解決過類似的問題的,指點一下。
3 總結
Android 4.X 系統加載 so 後,出現 java.lang.unsatisfiedlinkerror native method not found
的crash
的原因除了網上所說的 不存在這個so 和 方法名有問題(商用的應用應該不會有這個問題的)外,還有兩個原因:
- so 加載進系統的內存被系統由於資緊張而回收了,這種情況下直接再
load
一下so
文件就可以解決大部分; - so 文件有問題,這種情況下,可以通過重新從應用安裝目錄解壓出對應的
so
文件並重新加載來解決大部分;
這兩種方法不能保證可以100%解決問題,但可以減少大部分問題(90%);
Android 4.X 系統加載 so 失敗的原因分析