android熱修復——自己做個熱修復
阿新 • • 發佈:2019-01-06
類的載入機制
需要注意的地方
- 1.每次生成之後一定要測試;
- 2.儘量的不要分包,不要分多個dex
- 3.混淆的時候,設計到NDK AndFix.java 不要混淆
- 4.生成包之後一般會加固什麼的,這個時候生成的差分包,一定要在之前去生成。
- 5.既然是去修復方法,第一個不能增加成員變數,不能增加方法
原始碼
public class FixDexManager {
private Context mContext;
private File mDexFile;
private static final String TAG = FixDexManager.class.getSimpleName();
private Object applicationDexElements;
public FixDexManager(Context context) {
this.mContext = context;
//可以訪問的dex目錄
mDexFile = context.getDir("odex", Context.MODE_PRIVATE);
}
/**
* 修復dex包
*/
public void fixDex(String fixDexPath) throws Exception {
//2.獲取下載好的補丁 dexElement
//2.1移動到系統能夠訪問的 dex目錄下
File srcFile = new File(fixDexPath);
if (!srcFile.exists()) {
throw new FileNotFoundException(fixDexPath);
}
File destFile = new File(mDexFile, srcFile.getName());
if (destFile.exists()) {
Log.d(TAG, "patch [" + fixDexPath + "] has be loaded.");
return;
}
copyFile(srcFile, destFile);
//2.2ClassLoader讀取fileDex路徑
List<File> fixDexFiles = new ArrayList<>();
fixDexFiles.add(destFile);
}
/**
* 把dexElement注入到classLoader
*/
private void injectDexElement(ClassLoader classLoader, Object dexElements) throws Exception {
//1.先獲取pathList
Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
//所有屬性都可以獲得
pathListField.setAccessible(true);
Object pathList = pathListField.get(classLoader);
//2.獲得pathList中的dexElements
Field dexElementsFiled = pathList.getClass().getDeclaredField("dexElements");
dexElementsFiled.setAccessible(true);
//注入
dexElementsFiled.set(dexElementsFiled.get(pathList), dexElements);
}
/**
* 合併陣列
*/
private static Object combineArray(Object arrayLhs, Object arrayRhs) {
Class<?> localClass = arrayLhs.getClass().getComponentType();//獲得陣列物件
int i = Array.getLength(arrayLhs);
int j = Array.getLength(arrayRhs);
Object result = Array.newInstance(localClass, j);
for (int k = 0; k < j; ++k) {
if (k < i) {
Array.set(result, k, Array.get(arrayLhs, k));
} else {
Array.set(result, k, Array.get(arrayLhs, k - 1));
}
}
return result;
}
/**
* 將路徑複製到目標檔案下
*/
public static void copyFile(File src, File dest) throws IOException {
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
if (!dest.exists()) {
dest.createNewFile();
}
inChannel = new FileInputStream(src).getChannel();
outChannel = new FileOutputStream(dest).getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
} finally {
if (inChannel != null) {
inChannel.close();
}
if (outChannel != null) {
outChannel.close();
}
}
}
/**
* 從classLoader中獲取
*
* @param classLoader
*/
private Object getDexElementsByClassLoader(ClassLoader classLoader) throws Exception {
//1.先獲取pathList
Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
//所有屬性都可以獲得
pathListField.setAccessible(true);
Object pathList = pathListField.get(classLoader);
//2.獲得pathList中的dexElements
Field dexElementsFiled = pathList.getClass().getDeclaredField("dexElements");
dexElementsFiled.setAccessible(true);
return dexElementsFiled.get(pathList);
}
/**
* 載入全部的修復包
*/
public void loadFixDex() throws Exception {
File[] dexFiles = mDexFile.listFiles();
List<File> fixDexFiles = new ArrayList<>();
for (File dexFile : dexFiles) {
if (dexFile.getName().endsWith(".dex")) {
fixDexFiles.add(dexFile);
}
}
fixDexFiles(fixDexFiles);
}
/**
* 修復dex
*/
private void fixDexFiles(List<File> fixDexFiles) throws Exception {
//1.先獲取已執行的dexElement
ClassLoader applicationClassLoader = mContext.getClassLoader();
Object dexElements = getDexElementsByClassLoader(applicationClassLoader);
File optimizedDirectory = new File(mDexFile, "odex");
if (!optimizedDirectory.exists()) {
optimizedDirectory.mkdir();
}
//修復
for (File fixDexFile : fixDexFiles) {
ClassLoader fixDexClassLoader = new BaseDexClassLoader(
fixDexFile.getAbsolutePath(),//dex路徑 必須要在應用目錄下的dex檔案中
optimizedDirectory,//解壓路徑
null,//.so檔案的位置
applicationClassLoader//父classloader
);
Object fixDexElement = getDexElementsByClassLoader(fixDexClassLoader);
//3.把補丁的dexElement插到已經執行好的dexElements的最前面
//合併陣列
applicationDexElements = combineArray(dexElements, fixDexElement);
}
//注入到原來的類中applicationClassLoader
injectDexElement(applicationClassLoader, applicationDexElements);
}
}