Android so庫的熱更新問題
本來想寫資源的熱修復(fù)的,雖然方案差不多已經(jīng)完成了,但是考慮到一些敏感問題,資源修復(fù)就不寫了。那就來寫寫so的熱修復(fù),其原理和class的修復(fù)是一樣的,但是so的熱修復(fù)的需求并不高,就當(dāng)做學(xué)習(xí)吧。
首先來總結(jié)一下Android的ClassLoader方式的熱更新,這種方式類的查找過程是通過BaseDexClassLoader來完成的,最終會通過成員變量DexPathList對象中的findClass方法來查找類,代碼如下:
public Class findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
只需將patch的class插入到dexElements最前面即可完成熱更新,當(dāng)然還需要防止類被打上校驗的標(biāo)記,做法就是在class中插入一段字節(jié)碼引用其他dex中的類。
參考class的修復(fù)方式,我們可以在BaseDexClassLoader中找到加載so的邏輯。
@Override
public String findLibrary(String name) {
return pathList.findLibrary(name);
}
最終也會調(diào)用DexPathList對象中的方法進(jìn)行處理,其函數(shù)內(nèi)容為
public String findLibrary(String libraryName) {
String fileName = System.mapLibraryName(libraryName);
for (File directory : nativeLibraryDirectories) {
String path = new File(directory, fileName).getPath();
if (IoUtils.canOpenReadOnly(path)) {
return path;
}
}
return null;
}
可以看到邏輯和class是類似的,首先會調(diào)用System.mapLibraryName函數(shù)獲得so的名字,比如我傳入的參數(shù)是Test(這個Test就是在調(diào)用System.loadLibrary(“Test”)時傳入的),則這個函數(shù)的作用就是將其轉(zhuǎn)換為類似libTest.so這樣的名字,然后遍歷nativeLibraryDirectories數(shù)組,這是一個File文件夾數(shù)組,看其文件夾下是否存在對應(yīng)的so,并且是否可讀,如果滿足條件,則直接返回。
那么我們就可以將我們的patch的so所在目錄插入到這個數(shù)組最前面即可完成so的修復(fù)。具體代碼就不貼了,實踐后得出的結(jié)論是這種方式是完全可行的,只不過Android 6.0中這部分代碼邏輯發(fā)生了改變。
在Android 4.0-5.1中,只需要將文件夾目錄插入到nativeLibraryDirectories數(shù)組最前面即可,這個過程直接使用反射插入patch的so所在目錄到數(shù)組最前面。
/** List of native library directories. */ private final File[] nativeLibraryDirectories;
但是在Android 6.0中,查找邏輯轉(zhuǎn)為了Elements查找
/** List of native library path elements. */
private final Element[] nativeLibraryPathElements;
public String findLibrary(String libraryName) {
String fileName = System.mapLibraryName(libraryName);
for (Element element : nativeLibraryPathElements) {
String path = element.findNativeLibrary(fileName);
if (path != null) {
return path;
}
}
return null;
}
所以在6.0中需要將so的patch目錄轉(zhuǎn)換為Element對象,插入到nativeLibraryPathElements最前面,Element的對象可以直接用反射去實現(xiàn)下面的代碼進(jìn)行構(gòu)造即可。
//偽代碼,類不可見,需要用反射 Element e=new Element(fileDir, true, null, null)
當(dāng)然你也可以直接反射調(diào)用makePathElements方法創(chuàng)建Element數(shù)組。
最后的難點就是如何將對應(yīng)cpu類型的so拿到,這個過程還是十分復(fù)雜的,比如說一個so同時存在x86,armeabi-v7a,armeabi的patch,而手機(jī)cpu是armeabi-v7a的,這時候就應(yīng)該加載armeabi-v7a的so??傊@種情況組合起來會十分復(fù)雜了。
手機(jī)的cpu結(jié)構(gòu)類型可以通過Build.CPU_ABI和Build.CPU_ABI2拿到,后面做的事就是根據(jù)這兩個值去加載對應(yīng)目錄下的so,其實把這兩個目錄都插進(jìn)去就沒問題了。
總結(jié)
以上所述是小編給大家介紹的Android so庫的熱更新問題,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Android編程實現(xiàn)簡易彈幕效果示例【附demo源碼下載】
這篇文章主要介紹了Android編程實現(xiàn)簡易彈幕效果,涉及Android動畫及視圖動態(tài)操作相關(guān)技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2017-01-01
Android IPC機(jī)制Messenger實例詳解
這篇文章主要介紹了 Android IPC機(jī)制Messenger實例詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07
Android實現(xiàn)調(diào)用系統(tǒng)圖庫與相機(jī)設(shè)置頭像并保存在本地及服務(wù)器
這篇文章主要介紹了Android實現(xiàn)調(diào)用系統(tǒng)圖庫與相機(jī)設(shè)置頭像并保存在本地及服務(wù)器 ,需要的朋友可以參考下2017-03-03
Android編程之文件讀寫操作與技巧總結(jié)【經(jīng)典收藏】
這篇文章主要介紹了Android編程之文件讀寫操作與技巧,結(jié)合實例形式總結(jié)分析了Android常見的文件與目錄的讀寫操作,及相關(guān)函數(shù)的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2016-06-06
Android 單例模式實現(xiàn)可復(fù)用數(shù)據(jù)存儲的詳細(xì)過程
本文介紹了如何使用單例模式實現(xiàn)一個可復(fù)用的數(shù)據(jù)存儲類,該類可以存儲不同類型的數(shù)據(jù),并提供統(tǒng)一的接口來訪問這些數(shù)據(jù),通過雙重檢查鎖定機(jī)制,該類在多線程環(huán)境下是線程安全的,感興趣的朋友跟隨小編一起看看吧2025-02-02

