《Android---熱修復簡單探討(一)》------dex類加載方式

Android Class加載機制

使用該種方式實現就是要了解Android class的加載機制。然後利用反射進行hook。
在這裏插入圖片描述
通過類關係可以看出主要有,PathClassLoaderDexClassLoader兩個loader。

  • PathClassLoader:主要用於加載/data/app/中的apk
  • DexClassLoader:可以加載指定路徑的apk,dex,jar
    從源碼可以看出主要功能實現還是在BaseDexClassLoader中

BaseDexClassLoader中查找class方法

private final DexPathList pathList;
 ...
@Override
 protected Class<?> findClass(String name) throws ClassNotFoundException {
     List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
     // 該方法又調用了DexPathList中的方法
     Class c = pathList.findClass(name, suppressedExceptions);
     if (c == null) {
         ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
         for (Throwable t : suppressedExceptions) {
             cnfe.addSuppressed(t);
         }
         throw cnfe;
     }
     return c;
 }

DexPathList中查找class的代碼

private Element[] dexElements;
...
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) {
            	// 如果找到目標class就不會執行後面的把我們要修改的class放到前面即可完成替換
                return clazz;
            }
        }
    }
    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

Element則主要保存dex有關的信息,可以看看系統給我們的對象裏面都有哪些信息,對照着創建一個就好了。

實驗查看數據

 try {
   	ClassLoader classLoader = getClassLoader();
    L.i(classLoader.getClass().getSimpleName());

    Object pathList = ReflexUtils.getProperty(classLoader, "pathList");
    L.i(pathList.toString());
} catch (Exception e) {
    e.printStackTrace();
}
04-09 18:34:13.900 I/[TS-FH]: [ onCreate ]PathClassLoader(MainActivity.java:18)
04-09 18:34:13.901 I/[TS-FH]: [ onCreate ]DexPathList[[zip file "/data/app/com.wxfjava.struggle-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]](MainActivity.java:22)

打修復包

# --output=f.dex輸出文件名稱 源class,就目錄下所有class文件打包
dx --dex --output=f.dex **/*.class

Android中目錄說明

  • /system/app/系統自帶應用存儲位置
  • /data/app/用戶應用目錄
  • /data/data/應用數據存儲目錄
  • /data/dalvik-cache/dex文件存儲位置

應用安裝:將apk複製到/data/app/下,dex文件放到/data/dalvik-cache/目錄,並在數據目錄創建對應的數據信息。

代碼實現

try {
      // 將修復dex複製到工作目錄下
      File srcFile = new File(FIX_DEX_FILE);
      File dexFile = new File(dexPath.getAbsolutePath(), srcFile.getName());
      FileUtils.copyFile(srcFile, dexFile);

      ClassLoader pathClassLoader = mContext.getClassLoader();
      Object pathList = ReflexUtils.getProperty(pathClassLoader, "pathList");
      Object dexElements = ReflexUtils.getProperty(pathList, "dexElements");
      int length = Array.getLength(dexElements);

      DexClassLoader dexClassLoader = new DexClassLoader(dexFile.getAbsolutePath(), foptPath, null, pathClassLoader);
      Object fixPathList = ReflexUtils.getProperty(dexClassLoader, "pathList");
      Object fixDexElements = ReflexUtils.getProperty(fixPathList, "dexElements");
      int fixLength = Array.getLength(fixDexElements);

      Object newDexElements = Array.newInstance(Array.get(dexElements, 0).getClass(), length + fixLength);
      System.arraycopy(fixDexElements, 0, newDexElements, 0, fixLength);
      L.i("l:" + length + ",fixl:" + fixLength);
      System.arraycopy(dexElements, 0, newDexElements, fixLength, length);

      boolean result = ReflexUtils.setProperty(pathList, "dexElements", newDexElements);
      L.i(result + "-" + pathList.toString());
} catch (Exception e) {
      L.e(e);
}

後記

這種方式只能在有問題的class加載之前進行替換修復纔會有效。如果已經加載了有問題的class,進行替換是無效的,所以修復的代碼要放到Application中進行執行。
要實現實時修復,就要用到Andfix的實現方式了。直接修改問題方法的內存地址指向完成實時修復。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章