笔记 深入探索Android热修复技术原理

阿里电子书《深入探索Android热修复技术原理》整理的笔记


1.热修复技术介绍

  1. 代码修复两大主要方案
    • 底层替换方案:限制较多,但时效性好,立即见效
    • 类加载方案时效性差,需要重新冷启动才能见效,但限制少
  2. 代码修复底层替换方案
    • 底层替换方案是在已经加载了的类中直接替换掉原有方法.
    • 不能对原有类进行方法和字段的增减,因为这样将破坏原有类的结构.
    • 方法增减将导致这个类及整个Dex方法数的变化,伴随着方法索引的变化,这样访问方法时就无法正常的索引到正确的方法;
    • 字段增加或减少,所有字段的索引都会发生变化;
    • 传统底层替换方案,都是直接依赖修改虚拟机方法实体中的具体字段.依据的是Android开源版本.如果厂商修改了虚拟机方法实体,替换机制就可能出问题;
  3. 代码修复类加载方案
    • 类加载方案的原理是在app重新启动后让Classloader去加载新的类.
    • 在app运行到一半的时候,所有需要发生变更的类都已经被加载过,Android无法对一个类进行卸载.如果不重启,原来的类还在虚拟机中,就无法加载新类.
    • 只有在下次重启时候,在还没有走到业务逻辑前抢先加载补丁中的新类,后续访问才是新的类.
    • dex比较的最佳粒度,应该是在类的粒度
    • Sophix采用的也是全量合成dex的技术.可以看做是dex文件级别的类插装方案.Sophix对旧包与补丁包中classes.dex的顺序进行了打破与重组,使得系统可以自然地识别到这个顺序,以实现类覆盖的目的
  4. 资源修复
    • 市面上很多资源热修复方案都采用了Instant Run的实现
    • Instant Run中资源修复步骤:
      1. 构造一个新的AssetManager,通过反射调用addAssetPath,把这个完整的新资源包加到AssetManager中.这样就得到一个含有所有新资源的AssetManager.
      2. 找到所有之前引用到AssetManager的地方,通过反射,将引用出替换为AssetManager.
    • Sophix资源热修复没有采用Instant Run的技术,而是构造了一个package id 为 0x66 的资源包,这个包里只包含改变了的资源项,然后直接在原有AssetManager中addAssetPath这个包即可.无需变更AssetManager对象的引用.
      1. Sophix构造的补丁包的 package id 为0x66,不与已经加载的 0x7f冲突,所以直接加入到已有的AssetManager中可以直接使用.
      2. Sophix资源补丁包中,只包含新增资源,以及原有内容发生了改变的资源.
  5. SO库修复:本质上是对native方法的修复和替换

Sophix采用的是类似类修复反射注入方式,把补丁so库的路径插入到nativeLibraryDirectories数组的最前面, 这样加载so库的时候就是补丁so库而不是原来的so库

2.代码热修复技术

  1. 底层热替换原理
    • Android的java运行环境,在4.4以下用的是dalvik虚拟机,4.4以上是art虚拟机.
    • 在各种Android热修复方案中,Andfix即时生效.Andfix采用的方法是,在已经加载了的类中直接在native层替换掉所有方法,是在原来的类的基础上进行修改的.
    • 以art,Android6.0为例,每一个Java方法在art中都对应着一个ArtMethod对象,ArtMethod记录了这个Java方法的所有信息,包括所属类,访问权限,代码执行地址等.
    • Andfix会将一个旧Java方法对应的ArtMethod实例中的所有字段值替换为新方法的值,这样所有执行到旧方法的地方,都会取得新方法的执行入口,所属class,方法索引,所属dex.像调用旧方法一样执行了新方法的逻辑.
  2. 底层热替换兼容性根源
    • 市面上几乎所有的native替换替换,都是写死了ArtMethod结构体
    • 写死的ArtMethod结构和Android开源版本中完全一致,但各个厂家可以对ArtMethod进行修改,那么在修改过的设备上,市面上的native替换方案(将方案中写死了的ArtMethod关联的新方法的属性赋值到设备中的ArtMethod实例)就会出现问题,因为两个ArtMethod中相同字段的索引不同
  3. 突破底层热替换兼容问题
    • native层面替换,实质是替换ArtMethod实例的所有字段.
    • 只要把ArtMethod作为整体进行替换,即可解决兼容问题.
    • ArtMethod实例之间,是紧密线形排列的,所以一个ArtMethod的大小,就是其相邻的两个方法对应的ArtMethod实例的起始地址的差值.
  4. 包括Sophix在内的底层替换方案,都只能支持方法的替换,不支持补丁类中增减方法和字段
    • 补丁类中增减方法,会导致这个类及整个dex方法数的变化,方法数的变化伴随方法索引的变化,这样在调用方法时无法正常的所引导正确的方法.
    • 补丁类中增减字段,也会导致所有字段的索引发生变化.
  5. 你说不知的Java
    • 内部类在编译期会被编译为根外部类一样的顶级类;
    • 非静态内部类持有外部类的引用,静态内部类不持有外部类的引用.所以android性能优化中建议自定义Handler的实现尽量使用静态内部类,防止外部类Activity类不能被回收导致内存泄漏.
      自定义Handler使用静态内部类避免内存泄漏
    • 内部类和外部类之间,访问彼此的private属性及方法,编译期间:
      • 外部类访问内部类的私有成员及方法,编译期间自动为内部类生成access&**方法
      • 内部类访问外部类的private属性及方法,编译期间也会生成access&**方法提供给内部类
    • 同一个类及其内部类,如果老代码没有访问对方的私有属性/方法,新代码有访问对方的私有属性/方法,如果不能避免生成access&的生成,就会导致方法数的变化,导致热修复失败.避免生成access&方法需要:
      • 外部类所有的属性及方法改为public或protected;
      • 内部类所有的属性及方法改为public或protected;
    • 在编译期间,根据匿名内部类在外部类中出现的先后顺序,匿名内部类的名称依次累加:外部类名称&数字
      • 外部类名称是OutClass,其中对应的内部类在编译期间的名称依次是:OutClass&1,OutClass&2,-----
      • 为了实现热修复,外部类应该极力避免新增及减少匿名内部类;
      • 除非是新增匿名内部类到外部类的尾部,不会影响之前添加过的匿名内部类的名称,不然会导致热修复失效;
    • Java原始类型:double、float、byte、short、int、long、char、boolean
      • 如果一个常量的类型是Java原始类型,或String,为了优化性能,应该用static final修饰;
      • static final 引用类型,没有任何优化效果.
      • 因为 static final 修饰的原始类型及String常量,是在所属类的初始化时赋值,直接在内存中读取;
      • 而 static final 引用类型常量,初始化是在clinit方法中,本质上是通过sget-object指令去获取值,从虚拟机运行性能上无任何优化;
  6. 市面上的冷启动类加载实现方案
    • 1:采用dex插桩的方式,单独放一个帮助类在独立的dex中让其他类调用.最后加载补丁dex得到dexFile对象,将dexFile作为参数构建一个Element对象插入到dexElements数组最前面
    • 2:提供dex差量包,整体替换dex的方案:差量patch.dex和应用的classes.dex合成完整dex.加载完整dex得到dexFile对象,作为参数构建一个Element对象,然后整体替换掉旧的dexElements数组
    • 1的缺点是:Dalvik下影响类加载性能,Art下类地址写死,导致必须包含父类及引用,补丁包很大
    • 2的缺点是:dex的合并,内存消耗在 vm heap 上,容易导致OOM,合并失败
  7. Sophix采用的代码修复冷启动方案
    • Dalvik下使用全量Dex方案;
    • Art下本质上虚拟机已经支持多dex的加载,只要把补丁dex作为主dex(classes)即可
  8. Sophix在Dalvik下全量Dex方案思路
    • 基线包dex里面,去掉补丁包dex中包含的class;这样补丁+去除了补丁中包含类的基线包,就等于新app中所有类;
    • Sophix并没有把某个class的所有信息从基线dex中移除,仅仅移除了定义的入口,让解析基线dex时候找不到这个class的定义即可;这样不会导致dex的各个部分都发生变化,防止大量调整offset.
    • 只要把所有的dex都load进去,单个dex中不存在的类就可以在运行期间在其他dex中找到.补丁中的类和基线中的类可以互相访问到

3.资源热修复技术

Android资源的热修复,就是在app不重新安装的情况下,利用下发的补丁包直接更新app中的资源
Sophix的资源热修复方案

1:构造一个 package id 为0x66的资源包,这个包里只含有变更的资源,以及新增资源;
2:然后直接在原有AssetManager上调用addAssetPath加入这个资源包即可;

因为我们补丁包的id和已经加载的0x7f冲突,所以直接加入原有AssetManager即可直接使用

4.SO库热修复技术

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容