四、加載補丁
4.1 思路
通過上一篇博文,我們知道dex 保存在這個位置
```
BaseDexClassLoader–>pathList–>dexElements
```
apk 的classes.dex 可以從應用本身的DexClassLoader 中獲取。
path_dex 的dex 需要new 一個DexClassLoader 加載后再獲取。
分別通過反射取出dex 文件,重新合并成一個數組,然后賦值給盈通本身的ClassLoader 的dexElements
4.2 代碼實現
加載外部dex,我們可以在Application 中操作。
首先新建一個HotPatchApplication,然后在清單文件中配置,順便加上讀取sdcard 的權限,因為補丁就保存在那里。
HotPatchApplication 代碼如下:
```
package com.aitsuki.hotpatchdemo;
import android.app.Application;import android.os.Environment;import andr
oid.util.Log;import java.io.File;import java.lang.reflect.Array;import j
ava.lang.reflect.Field;import dalvik.system.DexClassLoader;
/**
* Created by hp on 2016/4/6.
*/
public class HotPatchApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 獲取補丁,如果存在就執行注入操作
String dexPath = Environment.getExternalStorageDirectory().getAb
solutePath().concat("/patch_dex.jar");
File file = new File(dexPath);
if (file.exists()) {
inject(dexPath);
} else {
Log.e("BugFixApplication", dexPath + "不存在");
}
}
/**
* 要注入的dex 的路徑
*
* @param path
*/
private void inject(String path) {
try {
// 獲取classes 的dexElements
Class<?> cl = Class.forName("dalvik.system.BaseDexClassLoader
");
Object pathList = getField(cl, "pathList", getClassLoader());
Object baseElements = getField(pathList.getClass(), "dexEleme
nts", pathList);
// 獲取patch_dex 的dexElements(需要先加載dex)
String dexopt = getDir("dexopt", 0).getAbsolutePath();
DexClassLoader dexClassLoader = new DexClassLoader(path, dexo
pt, dexopt, getClassLoader());
Object obj = getField(cl, "pathList", dexClassLoader);
Object dexElements = getField(obj.getClass(), "dexElements",
obj);
// 合并兩個Elements
Object combineElements = combineArray(dexElements, baseElemen
ts);
// 將合并后的Element 數組重新賦值給app 的classLoader
setField(pathList.getClass(), "dexElements", pathList, combin
eElements);
//======== 以下是測試是否成功注入=================
Object object = getField(pathList.getClass(), "dexElements",
pathList);
int length = Array.getLength(object);
Log.e("BugFixApplication", "length = " + length);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
/**
* 通過反射獲取對象的屬性值
*/
private Object getField(Class<?> cl, String fieldName, Object object)
throws NoSuchFieldException, IllegalAccessException {
Field field = cl.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(object);
}
/**
* 通過反射設置對象的屬性值
*/
private void setField(Class<?> cl, String fieldName, Object object, O
bject value) throws NoSuchFieldException, IllegalAccessException {
Field field = cl.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
}
/**
* 通過反射合并兩個數組
*/
private Object combineArray(Object firstArr, Object secondArr) {
int firstLength = Array.getLength(firstArr);
int secondLength = Array.getLength(secondArr);
int length = firstLength + secondLength;
Class<?> componentType = firstArr.getClass().getComponentType();
Object newArr = Array.newInstance(componentType, length);
for (int i = 0; i < length; i++) {
if (i < firstLength) {
Array.set(newArr, i, Array.get(firstArr, i));
} else {
Array.set(newArr, i, Array.get(secondArr, i - firstLengt
h));
}
}
return newArr;
}
}
```
- 第一章 熱修復設計
- 第一節、AOT/JIT & dexopt 與dex2oat
- 一、AOT/JIT
- 二、dexopt 與dex2oat
- 第二節、熱修復設計之CLASS_ISPREVERIFIED 問題
- 一、前言
- 二、建立測試Demo
- 三、制作補丁
- 四、加載補丁
- 五、CLASS_ISPREVERIFIED
- 第三節、熱修復設計之熱修復原理
- 一、Android 熱修復
- 二、Android 虛擬機和編譯加載順序
- 三、混合模式的理解
- 四、源碼類到機器執行的文件過程
- 五、補丁包
- 六、類補丁生效原理
- 七、Davlik 虛擬機的限制
- 八、Davlik Class resolved by unexpected DEX: 限制和處理方式
- 九、類加載器的雙親委派加載機制
- 第四節、Tinker 的集成與使用(自動補丁包生成)
- 一、簡述
- 二、Tinker 組件依賴
- 三、Tinker 的配置及任務
- 四、Tinker 封裝與拓展
- 五、編寫Application 的代理類
- 六、常用API
- 七、測試
- 八、細節
- 第二章 插件化設計
- 第一節、Class 文件與Dex 文件的結構解讀
- 一、Class 文件
- 二、Dex 文件
- 三、Class 文件和Dex 文件對比
- 第二節、Android 資源加載機制詳解
- 第三節、四大組件調用原理
- 第四節、so 文件加載機制
- 第五節、Android 系統服務實現原理
- 第三章 組件化框架設計
- 第一節、阿里巴巴開源路由框——ARouter 原理分析
- 第二節、APT 編譯時期自動生成代碼&動態類加載
- 第三節、Java SPI 機制
- 第四節、AOP&IOC
- 第五節、手寫組件化架構
- 第四章 圖片加載框架
- 第一節 圖片加載框架選型
- 第二節 Glide 原理分析
- 第三節 手寫圖片加載框架實戰
- 第五章 網絡訪問框架設計
- 第一節 網絡通信必備基礎
- 第二節 OkHttp 源碼解讀
- 第三節 Retrofit2 源碼解析
- 第六章 RXJava響應式編程框架設計
- 第一節 RXJava之鏈式調用
- 第二節 RXJava之擴展的觀察者模式
- 第三節 RXJava之事件變換設計
- 第四節 Scheduler 線程控制
- 第七章 IOC架構設計
- 第一節 依賴注入與控制反轉
- 第二節 ButterKnife 原理上篇、中篇、下篇
- 第三節 IOC架構設計之Dagger2架構設計
- 第八章 Android架構組件 JetPack
- 第一節 LiveData的工作原理
- 第二節 Navigation 如何解決tabLayout 問題
- 第三節 ViewModel 如何感知View 生命周期及內核原理
- 第四節 Room 架構方式方法
- 第五節 dataBinding 為什么能夠支持MVVM
- 第六節 WorkManager 內核揭秘
- 第七節 Lifecycles 生命周期