e-works数字化企业网  »  文章频道  »  基础信息化  »  移动应用

Android热修复技术总结

2017/9/11    来源:云栖社区    作者:佚名      
关键字:Android  Android插件  
插件化和热修复技术是Android开发中比较高级的知识点,是中级开发人员通向高级开发中必须掌握的技能,插件化的知识可以查我我之前的介绍:Android插件化。本篇重点讲解热修复,并对当前流行的热修复技术做一个简单的总结。
    插件化和热修复技术是Android开发中比较高级的知识点,是中级开发人员通向高级开发中必须掌握的技能,插件化的知识可以查我我之前的介绍:Android插件化。本篇重点讲解热修复,并对当前流行的热修复技术做一个简单的总结。
 
    热修复
 
    什么是热修复?
 
    简单来讲,为了修复线上问题而提出的修补方案,程序修补过程无需重新发版!
 
    技术背景
 
    在正常软件开发流程中,线下开发->上线->发现bug->紧急修复上线。不过对于这种方式代价太大。
 
    Android热修复技术总结
 
    而热修复的开发流程显得更加灵活,无需重新发版,实时高效热修复,无需下载新的应用,代价小,最重要的是及时的修复了bug。
 
    Android热修复技术总结
 
    当前热门的热修复技术
 
    当前热门的热修复技术有:
 
  • QQ空间超级补丁、微信Tinker
  
  • 饿了么Amigo
 
  • 美团Robust
 
  • 360RePlugin
 
  • 滴滴出行VirtualAPK
    
  • ...
 
    热修复技术
 
    要弄清热修复技术的原理,就要先弄清Android的ClassLoader机制,相关文章可以阅读之前的介绍:ClassLoader类加载机制。Android的ClassLoader分为PathClassLoader和DexClassLoader,它们都都继承自BaseDexClassLoader,其中PathClassLoader用来加载系统类和应用类;DexClassLoader用来加载jar、apk、dex文件。例如下面要介绍的阿里的Andfix和Sophix的原理如下:
 
    AndFix
 
    AndFix:由补丁类的classLoader加载补丁类,在native层针对不同Android架构中的不同的ArtMethod结构调用对应的replaceMethod方法按照定义好的ArtMethod结构一一替换方法的所有信息如所属类、访问权限、代码内存地址等。
 
    稳定性较差,会受到国内ROM厂商对ArtMethod结构更改的影响,所以这正是AndFix不支持很多机型的原因。
 
    Sophix
 
    Sophix:由补丁类的classLoader加载补丁类,在native层直接memcpy(smeth,dmth,sizeof(ArtMethod))替换整个artMethod的结构。初始化类时会为这个类分配空间,AllocArtMethodArray会紧挨着的new出来放入art中的方法数组中。通过计算辅助类的前后两个方法的起始地址就可以计算出artMethod结构的大小了。
 
    注:补丁类初始化时,也会分配自己的artMethod空间,拿这个修复过的新ArtMethod去替换旧ArtMethod的内容,不用管ArtMethod的结构。稳定性大大提高!
 
    java
 
    内部类编译
 
    静态内部类/非静态内部类区别
 
    内部类会被编译器生成同外部类一样的顶级类。只不过非静态内部类会持有外部类的引用。这也是Android性能优化建议Handler使用静态内部类,防止外部类Activity不能被回收导致造成OOM。
 
    内部类和外部类互相访问
 
    内部类和外部类互相访问private方法和字段时,会自动在对应类为对方生成public的access&**方法。
 
    热部署解决方案
 
    外部类如果有内部类把所有的field/method的private访问权限改成proteced或者public内部类将所有的field/method的private访问权限改成proteced或者public。
 
    匿名内部类编译
 
    匿名内部类命名规则
 
    外部类&number。number即编译器根据匿名内部类出现在外部类中的顺序,依次累加。
 
    热部署解决方案
 
    新增/减少匿名内部类对热部署是无解的,因为补丁修复工具拿到的是class文件,无法区别DexFileDemo&1和DexFileDemo&2,会导致类的顺序乱套。如果匿名内部类插入到末尾则是允许。
 
    域编译
 
    静态field,非静态field编译
 
    热部署不支持field/method增加和删除和 clinit方法的修改,静态field的初始化和静态代码块会被编译在编译器合成的方法clinit中,非静态字段的初始化会被编译在编译器生成的init无参构造函数中,
 
    静态field,静态代码块
 
    clinit方法会在类加载阶段的类初始化时调用,clinit中静态field和静态代码块的出现顺序就是二者在源码中出现的顺序。因为类已经加载过了,所以就算修复了clinit方法也不会生效了。
 
    dvmResolveClass->dvmLinkClass->dvmInitClass,然后执行clinit方法
 
    以下情况会去加载一个类
 
    1.new 一个类的对象时new instance
 
    2.调用类的静态方法(invoke static)
 
    3.获取类的静态域的值(sget)
 
    非静态field,非静态代码块
 
    类的构造函数会被编译器翻译成init方法,会先进行非静态field和非静态代码块的初始化。它们出现的顺序也是和在源码中出现的顺序一样。
 
    执行new instance指令时,如果类没有加载过,就尝试加载类。然后对对象内存分配,再然后执行invoke direct指令调用类的init构造函数进行初始化
 
    热部署解决方案
 
    不支持对静态字段和静态代码块的修改,会导致热部署失败,只能冷启动生效。支持非静态字段和非静态代码块修改,热部署只是将init构造函数作为普通的方法变更。
 
    final static 域编译
 
    final static 域编译规则
 
    final static引用类型初始化仍在clinit中final static基本类型和String类型,类加载初始化dvminitClass在执行clinit方法之前,先执行initSFields,这个方法为static域赋予默认值。引用类型默认NULL,final static修饰的基本类型和String类型会在这里初始化赋值。
 
    final static 域优化原理
 
    final static基本类型执行const/4指令,操作数在dex中的位置(encoded_array_item)就是在opcode后一个字节。
 
    final static String类型执行const-string指令,本质同上只不过拿到的是字符串常量在dex文件结构中字符串常量区的索引id。dex文件有一块区域存储所有的字符串常量会被完整的加载到虚拟机内存中-字符串常量区。
 
    final static引用类型执行sget指令,首先调用dvmDexGetResolveField看这个域是否之前解析过,没有的话调用dvmDexResolveField尝试解析域,如果这个静态域所在的类没有解析过,尝试调用dvmResolveClass,拿到这个sField,然后通过dvmDexGetResolveField(sField)获取这个静态值。
 
    热部署解决方案
 
    final static基本类型/string类型最终引用的类型会被热部署替换掉。
 
    final static引用类型因为会被翻译到clinit方法中,热部署失败。
 
    泛型编译
 
    为什么需要泛型
 
    Java泛型完全有编译器实现,由编译器执行类型检查和类型推断,生成非泛型字节码,称之为擦除。
 
    没有泛型之前想要实现类泛型,利用所有类的父类时Object进行强转,这完全依赖程序员的自主性,很容易出现ClassCastException。泛型的出现解决了类型检查和类型推断的问题。
 

责任编辑:李欢
本文为授权转载文章,任何人未经原授权方同意,不得复制、转载、摘编等任何方式进行使用,e-works不承担由此而产生的任何法律责任! 如有异议请及时告之,以便进行及时处理。联系方式:editor@e-works.net.cn tel:027-87592219/20/21。
兴趣阅读
相关资料
e-works
官方微信
掌上
信息化
编辑推荐
新闻推荐
博客推荐
视频推荐