轉載請以鏈接形式標明出處:
本文出自:103style的博客
效果圖
實現思路
主要實現思路主要是:
-
先編寫一個有 bug 的程序, 運行安裝到手機。
-
修正bug之後,重新
rebuild
, 然後找到app - build - intermediates - dex - debug - mergeProjectDexDebug - out - classes.dex
移動到 修復包 下載的目錄 , 這裏放在assets
目錄下,並重命名classes.dex
爲classes2.dex
。
-
然後點擊程序上的
Move Dex
, 將修正bug之後的dex包 移動到android/data/packagename/
目錄下,在這裏目錄纔有加載dex權限
。 -
然後重啓程序,在繼承自
MultiDexApplication
的Application
中加載對應的dex
文件,獲取對應的dexElements
,然後合併到應用的dexElements
之前。
相關代碼
-
MyApplication
public class MyApplication extends MultiDexApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); new FixDemo().loadFixedDex(base); } }
-
MainActivity
public class MainActivity extends AppCompatActivity { private TextView bugTv; private int i = 10; private int a = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bugTv = findViewById(R.id.bug); bugTv.setText("Bug: " + i + " / " + a); findViewById(R.id.bug).setOnClickListener(v -> bugMethod()); findViewById(R.id.move).setOnClickListener(v -> moveDex("classes2.dex")); } private void bugMethod() { Toast.makeText(this, "res = " + i / a, Toast.LENGTH_SHORT).show(); } private void moveDex(String name) { //目錄 data/data/packageName/odex File fileDir = getDir(FixDemo.DEX_DIR, Context.MODE_PRIVATE); AssetManager am = getResources().getAssets(); try { InputStream is = am.open(name); String filePath = fileDir.getAbsolutePath() + File.separator + name; File file = new File(filePath); if (file.exists()) { file.delete(); } FileOutputStream os = new FileOutputStream(filePath); int len; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } os.close(); is.close(); //粘貼完文件 File f = new File(filePath); if (f.exists()) { //文件從sk卡賦值到應用運行目錄下,成功則toast提示 Toast.makeText(this, "dex移動成功,請重啓應用", Toast.LENGTH_SHORT).show(); } } catch (IOException e) { e.printStackTrace(); } } }
-
FixDemo
public class FixDemo { public static String DEX_DIR = "odex"; private File odexDir; /** * 開始替換dex */ public void loadFixedDex(Context context) { if (null == context) { return; } //遍歷所有的修復的dex odexDir = context.getDir(DEX_DIR, Context.MODE_PRIVATE); File[] listFiles = odexDir.listFiles(); if (listFiles == null) { return; } HashSet<File> loadedDex = new HashSet<>(); for (File file : listFiles) { if (file.getName().startsWith("classes") || file.getName().endsWith(".dex")) { //先將補丁文件放到一個集合裏,然後再進行合併 loadedDex.add(file); } } //dex合併 doDexInject(context, loadedDex); } /** * dex 合併 */ private void doDexInject(Context appContext, HashSet<File> loadedDex) { try { String filesDirPath = odexDir.getAbsolutePath() + File.separator + "opt_dex"; File fopt = new File(filesDirPath); if (!fopt.exists()) { fopt.mkdir(); } //1.加載應用程序的dex BaseDexClassLoader pathLoader = (BaseDexClassLoader) appContext.getClassLoader(); for (File dex : loadedDex) { //2.加載指定的修復的dex文件 DexClassLoader classLoader = new DexClassLoader( dex.getAbsolutePath(), fopt.getAbsolutePath(), null, pathLoader); //3.合併 Object dexObj = getPathList(classLoader); Object pathObj = getPathList(pathLoader); Object mDexElementsList = getDexElements(dexObj); Object pathDexElementsList = getDexElements(pathObj); //將兩個list合併爲一個 Object dexElements = combineArray(mDexElementsList, pathDexElementsList); //重寫給PathList裏面的Element[] dexElements賦值 Object pathList = getPathList(pathLoader); ReflectUtils.setClassFiled(pathList, pathList.getClass(), "dexElements", dexElements); } } catch (Exception e) { e.printStackTrace(); } } private Object getPathList(ClassLoader classLoader) { try { return ReflectUtils.getDeclaredField(classLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList"); } catch (ClassNotFoundException e) { e.printStackTrace(); return null; } } private Object getDexElements(Object obj) { return ReflectUtils.getClassField(obj, "dexElements"); } /** * 合併數組 */ private Object combineArray(Object arrayLbs, Object arrayRhs) { Class localClass = arrayLbs.getClass().getComponentType(); int i = Array.getLength(arrayLbs); int j = i + 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(arrayLbs, k)); } else { Array.set(result, k, Array.get(arrayRhs, k - i)); } } return result; } }
以上