关于虚拟机
以Java虚拟机(JVM)为例,它的工作流程大致如下:
(3) 通过执行引擎与操作系统提供的接口交互。
以Davlik为例,与JVM主要存在三个区别:
(2) 应用体积更小,借助dx工具将class转成dex文件的过程中,会对代码进行一些优化,比如一些重复的方法等只会保留一份,所以体积会变小。
(3) 运行速度更快,在JVM中方法的调用主要是基于栈实现的,所以需要大量的入栈出栈,而Dalvik则是基于寄存器实现的,因此速度会更快,性能会有明显的提升。
关于类加载机制
(5) DexPathList:主要负责解析dex并以一个Element数组存储dex信息。
e. 从Element中取出DexFile,并调用其loadClassBinaryName完成类的加载。
首先我们看一下ClassLoader中的loadClass方法是如何实现的,如上图所示类的加载是基于双亲委托机制实现的,大致可以分为三步:
(a) 检查class是否被加载过。
(b) 判断parent是否为空,决定是调用BootClassLoader还是parent的loadClass方法。
(c) 如果前两步还没加载成功,则自己进行查找。
为什么要使用双亲委托机制呢?主要是考虑到了两方面的原因:
我们的目的就是基于虚拟机的类加载机制,实现一个简单的热修复。看到这里相信大家都已经有思路了,既然是遍历数组,那么我们就可以通过在数组的第一个位置插入一个新的dex数据实现热修复。
热修复的简单实现
我们自己创建一个安卓应用,自己定义一个TestUtil类并实现一个test方法,抛出一个异常,关键代码如下:
(2) 用于修复异常的dex文件
存在的安全性问题
b. 获取到类加载器的Class;
c. 反射获取DexPathList对象pathList;
d. 反射获取Element数组dexElements;
e. 获取补丁数组;
f. 合并两个数组;
g. 替换dexElements为合并之后的数组;
h. 调用安装补丁的方法。package com.android.hotfix; import android.app.Application; import java.io.File;import java.io.IOException;import java.lang.reflect.Array;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List; public class HotFix { public static void installDex(Application myApp, String path){ //获取类加载器 ClassLoader loader = myApp.getClassLoader(); //获取到类加载器的Class Class cls = loader.getClass(); //获取pathList对象 Field plField = null; Object pathList = null; try { plField = cls.getSuperclass().getDeclaredField("pathList"); plField.setAccessible(true); pathList = plField.get(loader); } catch (Exception e) { e.printStackTrace(); } //获取Element数组 Object[] dexElements = null; Field dexField = null; if(pathList!=null){ try { dexField = pathList.getClass().getDeclaredField("dexElements"); dexField.setAccessible(true); dexElements = (Object[]) dexField.get(pathList); } catch (Exception e) { e.printStackTrace(); } } //获取补丁数组 Object[] patchElements = makePatch(myApp,pathList,path); if(patchElements!=null && dexElements !=null){ //合并两个数组 Object[] newElements = (Object[]) Array.newInstance(dexElements[0].getClass(),dexElements.length+patchElements.length); System.arraycopy(patchElements,0,newElements,0,patchElements.length); System.arraycopy(dexElements,0,newElements,patchElements.length,dexElements.length); //替换合并后的数组 try { dexField.set(pathList,newElements); } catch (Exception e) { e.printStackTrace(); } try { Object[] testField = (Object[]) dexField.get(pathList); } catch (IllegalAccessException e) { e.printStackTrace(); } } } private static Object[] makePatch(Application myApp, Object pathList, String path) { Object[] rst = null; try { //获取makeElements方法 //Method makeMethod = pathList.getClass().getDeclaredMethod("makePathElements", java.util.List.class,java.io.File.class,java.util.List.class); Method makeMethod = pathList.getClass().getDeclaredMethod("makeElements", java.util.List.class,java.io.File.class,java.util.List.class,boolean.class,ClassLoader.class); //参数准备 List<File> dexFiles = new ArrayList<>(); File dexFile = new File(path); dexFiles.add(dexFile); File optimizedDirectory = myApp.getCacheDir(); List<IOException> suppressedExceptions = new ArrayList<>(); //调用方法 if(makeMethod!=null) { makeMethod.setAccessible(true); //return (Object[]) makeMethod.invoke(pathList,dexFiles,optimizedDirectory,suppressedExceptions); return (Object[]) makeMethod.invoke(pathList,dexFiles,optimizedDirectory,suppressedExceptions,false,myApp.getClassLoader()); } } catch (Exception e) { e.printStackTrace(); } return null; }}
protected void attachBaseContext(Context base) { super.attachBaseContext(base); String dexPath = "/storage/emulated/0/Android/data/classes.dex"; File file = new File(dexPath); if(file!=null && file.exists()) { HotFix.installDex(this, dexPath); } }
https://mp.weixin.qq.com/s/8tMSPl5RD6H9TK2WT3Hfkg
接口原型 - IT教程,计算机教程,API相关DEMO