Android热修复相关
Robust | 字节码插桩 代理 自动埋点 |
---|---|
Tinker | dex差分(bsdiff差分不关心文件格式 二进制全格式) 反射 类加载 |
Qzone | dex差分 反射 类加载 |
-
java中的类加载 双亲委托
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name);//判断类是否已经加载过 if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false);//父类加载器优先加载 } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name);//调用当前类加载器的findClass方法进行加载 // this is the defining class loader; record the stats } } return c; }
-
android中的类加载器
- BootClassLoader 系统类
- PathClassLoader
- DexClassLoader
PathClassLoader中反射获取makePathElements()方法 得到补丁包Element[]
将新补丁包与原包合并 通过System.arraycopy(...)
CLASS_ISPREVERIFIED 标记错误 导致修复异常
使用javaessit进行字节码插桩 让原来的所有class都引用补丁的类
img
gradle插桩
类比为 用 gson,fastjson这类第三方框架来修改json文件。我们也可以利用 特定的手段来自由修改class文件。这类技术框架有ASM,AspectJ, Javassist等。 由于我们androidStudio用gradle来构建项目,所以,还需要我们自定义gradle插件,来在合适的时机 使用ASM 这种技术框架来在class文件中修改字节码内容。
javac命令之后,dx命令之前
gradle执行项目构建,是通过一个一个的task来进行。比如 将java文件用javac命令编译为 class,任务名字叫做::app:compileDebugJavaWithJavac
img
gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
@Override
void beforeEvaluate(Project project) {
println " add project evaluation lister beforeEvaluate,project path is: "+project
}
@Override
void afterEvaluate(Project project, ProjectState state) {
println " add project evaluation lister afterProject,project path is:"+project
}
}
在afterEvaluate解析完成之后执行
anroid.getApplicationVariants().all{
variant ->
String variantName = variant.getName(); //debug 或者 release
String capitializeName = variantName.capitalize();//首字母大写
Task dexTask = project.getTasks().findByName("transformClassWithDexBuilderFor" + capitializeName);
dexTask.doFrist{....打包任务之前进行插桩}
}
ASM进行插桩
ClassReader cr = new ClassReader(inputStream);
ClassWriter cw = new ClassWriter(cr,0);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5,cw){
public MethodVisitor visitMethod( final int access,final String name,final String desc,final String signature, final String[] exceptions) {
MethodVisitor mv = super.visiMethod(access,name,desc,signature,exceptions);
mv = new MethodVisitor(Opcodes.ASM4,mv){
visitlnsn(int opcode){
//在构造方法中插入类的引用
if("<init>".equals(name)&&opcode==Opcodes.RETURN){
.....
}
super.visitlnsn(opcode);
}
}
return mv;
}
}
cr.accept(cv,0);//启动分析
Android N 混合编译
AOT提前编译 导致类被加载无法被替换
解决方式 使用运行时替换PathClassloader
Thread.currentThread().setContextClassloader(classLoader);
LoaderApk+Resources+DrawableInflater(参考Tinker)