Android热修复相关

Android热修复相关

Robust 字节码插桩 代理 自动埋点
Tinker dex差分(bsdiff差分不关心文件格式 二进制全格式) 反射 类加载
Qzone dex差分 反射 类加载
  1. 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;
        }
    
  2. 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)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容