Android热修复技术总结

  • 时间:
  • 浏览:0
  • 来源:大发五分时时彩—大发分分时时彩

2.而且厂商的自定义ROM,对少数机型暂不支持。兼容性差。

综上,对于上面的几种框架技术总结如下:

Java泛型完全有编译器实现,由编译器执行类型检查和类型推断,生成非泛型字节码,称之为擦除。

没人泛型完后 让你实现类泛型,利用所有类的父类时Object进行强转,这完全依赖守护程序员的自主性,很容易突然总出 ClassCastException。泛型的突然总出 出理 了类型检查和类型推断的问題图片。

Sophix:由补丁类的classLoader加载补丁类,在native层直接memcpy(smeth,dmth,sizeof(ArtMethod))替换整个artMethod的型态。初始化类不会 为这种类分配空间,AllocArtMethodArray会紧挨着的new出来倒入art中的措施数组中。通过计算辅助类的前后另一三个 措施的起始地址就都可不可以 计算出artMethod型态的大小了。

注:补丁类初始化时,也会分配买车人的artMethod空间,拿这种修复过的新ArtMethod去替换旧ArtMethod的内容,不用管ArtMethod的型态。稳定性大大提高!

在Android热修复的过程中,不仅时要对错误的代码进行修复,还时要对资源文件进行修复。目前市面上的资源热修复方案基本上不会 参考Instant Run的实现。Instant Run实现过程要花费分为两部:

1、构造另一三个 新的AssetManager,并通过反射条用addAssetPath,把这种完全的新资源包加入到AssetManager中。没人 就得到了另一三个 蕴藏所有新资源的AssetManager。

针对现在市面上比较流行的热修复方案,这里选者Sophix、QQ超级补丁和Tinker进行简单的介绍。前面说过,例如于qq空间和微信的实现措施都时要重新启动不用 修复bug,而阿里的Sophix采用的是非浸入式的措施不时要冷启动。

微信针对QQ空间超级补丁技术的缺陷提出了另一三个 提供DEX差量包,整体替换DEX的方案。主要的原理是与QQ空间超级补丁技术基本相同,区别在于不再将patch.dex增加到elements数组中,统统 差量的措施给出patch.dex,而且将patch.dex与应用的classes.dex合并,而且整体替换掉旧的DEX文件,以达到修复的目的。其原理图如下:

而热修复的开发流程显得更加灵活,不用重新发版,实时高效热修复,不用下载新的应用,代价小,最重要的是及时的修复了bug。

类的构造函数会被编译器翻译成init措施,会先进行非静态field和非静态代码块的初始化。它们突然总出 的顺序也是和在源码中突然总出 的顺序一样。

执行new instance指令时,而且类没人加载过,就尝试加载类。而且对对象内存分配,再而且执行invoke direct指令调用类的init构造函数进行初始化

不过微信的方案仍然会有如下问題图片:

sophix采用的也是全量合成dex的技术,这种技术是从手淘插件化框架Atlas汲取的。直接利用Android没人 的类查找和合成机制,快速合成新的全量dex。没人一来,亲们既不时要出理 合成时措施数超过的情况,对于dex的型态统统 用进行破坏性重构。

父类是泛型类有setNumber(T value),子类想override setNumber(Number value)。然而实际父类的措施实际是setNumber(Object value),子类想重写却变成了重载,这就突然总出 了类型擦除和多态之间的冲突。然而编译器自动帮亲们合成了Bridge措施实现了重载,在子类中生成了相同签名bridge措施,组织组织结构实际调用子类的重写措施。

当前热门的热修复技术有:

so库的修复本质上是对native措施的修复和替换。亲们知道在JNI编程中,native措施都可不可以 通过动态注册和静态注册三种 措施进行。动态注册的native措施时要实现JNI_OnLoad措施,共同实现另一三个 JNINativeMethod[]数组,静态注册的native措施时然后Java+类完全路径+措施名的格式。

3.在ART模式下,而且类修改了型态,就会突然总出 内存错乱的问題图片。为了出理 这种问題图片,就时要把所有相关的调用类、父类子类等等完全加载到patch.dex中,原因分析补丁包异常的大,进一步增加应用启动加载的完后 ,耗时更加严重。

传统的底层替换措施,不论是Dexposed、Andfix而且统统安全界的Hook方案,不会 直接依赖修改虚拟机措施实体的具体字段。例如,改Dalvik措施的jni函数指针、改类或措施的访问权限等等。没人 就带来另一三个 很严重的问題图片,而且Android是开源的,各个手机厂商都都可不可以 对代码进行改造,而Andfix里ArtMethod的型态是根据公开的Android源码中的型态写死的。而且某个厂商对这种ArtMethod型态体进行了修改,就和没人 开源代码里的型态不一致,没人在这种修改过了的设备上,通用性的替换机制就会出问題图片。这便是不稳定的根源。

这是例如方案的固有限制,而底层替换方案最为人诟病的地方,在于底层替换的不稳定性。

参考:http://www.jianshu.com/p/ed03e8e4b08f

Tinker集成Sophix集成

新增lamada表达式会原因分析组织组织结构类新增另一三个 辅助措施。修改的lamda表达式逻辑引用了组织组织结构变量,会原因分析辅助类持有了组织组织结构对象,会新增这种组织组织结构对象的变量。也是会原因分析热修复失败。

2.为了实现修复这种过程,时要在应用中加入另一三个 dex!dalvikhack.dex中只有另一三个 类,对性能影响不大,而且对于patch.dex来说,修复的类到了一定数量,就时要花不少的时间加载。对手淘这种航母级应用来说,启动耗时增加2s以上是只有够接受的事。

类加载方案的原理是在app重新启动后让Classloader去加载新的类。而且在app运行到一半的完后 ,所有时要居于变更的类而且被加载过了,在Android上是无法对另一三个 类进行卸载的。而且不重启,没人 的类还在虚拟机中,就无法加载新类。而且,只有在下次重启的完后 ,在还没走到业务逻辑完后 抢先加载补丁中的新类,没人 后续访问这种类时,就会Resolve为新类。从而达到热修复的目的。

没人一来,亲们不仅出理 了兼容性问題图片,而且而且忽略了底层ArtMethod型态的差异,对于所有的Android版本不会 再时要区分,代码量大大减少。即使完后 的Android版本不断修改ArtMethod的成员,假如有一天保证ArtMethod数组仍是以线性型态排列,就能直接适用于将来的Android 8.0、9.0等新版本,不用再针对新的系统版本进行适配了。

编译器而且发现变量声明打上去了泛型信息,编译器自动打上去了check-cast的强制转换,而且编译器会为泛型做类型检查,统统自动的强制转换不用突然总出 ClassCastException。

我人太好都可不可以 很大地节省空间,但而且对dex内容的比较粒度过细,实现较为繁复,性能消耗比较严重。实际上,dex的大小占整个apk的比例是比较低的,另一三个 app上面的dex文件大小并不会 主要每种,而占空间大的主要还是资源文件。而且,Tinker方案的时光图片 代价转换的性价比不高。

要弄清热修复技术的原理,就要先弄清Android的ClassLoader机制,相关文章都可不可以 阅读完后 的介绍:ClassLoader类加载机制。Android的ClassLoader分为PathClassLoader和DexClassLoader,它们都都继承自BaseDexClassLoader,其中PathClassLoader用来加载系统类和应用类;DexClassLoader用来加载jar、apk、dex文件。例如下面要介绍的阿里的Andfix和Sophix的原理如下:

一旦补丁类中突然总出 了措施的增加和减少,就会原因分析这种类以及整个Dex的措施数的变化。措施数的变化伴随着措施索引的变化,没人 在访问措施时就无法正常地索引到正确的措施了。

final static基本类型/string类型最终引用的类型会被热部署替换掉。

final static引用类型而且会被翻译到clinit措施中,热部署失败。

final static引用类型初始化仍在clinit中final static基本类型和String类型,类加载初始化dvminitClass在执行clinit措施完后 ,先执行initSFields,这种措施为static域赋予默认值。引用类型默认NULL,final static修饰的基本类型和String类型会在这里初始化赋值。

底层替换方案是在而且加载了的类中直接替换掉原有措施,是在没人 类的基础上进行修改的。因而无法实现对与原有类进行措施和字段的增减,而且没人 将破坏原有类的型态。

在正常软件开发流程中,线下开发->上线->发现bug->紧急修复上线。不过对于这种措施代价越多。

简单来讲,为了修复线上问題图片而提出的修补方案,守护程序修补过程不用重新发版!

插件化和热修复技术是Android开发中比较高级的知识点,是中级开发人员通向高级开发中时要掌握的技能,插件化的知识都可不可以 查我我完后 的介绍:Android插件化。本篇重点讲解热修复,并对当前流行的热修复技术做另一三个 简单的总结。

clinit措施会在类加载阶段的类初始化时调用,clinit中静态field和静态代码块的突然总出 顺序统统 二者在源码中突然总出 的顺序。而且类而且加载过了,统统就算修复了clinit措施统统 会生效了。

dvmResolveClass->dvmLinkClass->dvmInitClass,而且执行clinit措施

以下情况会去加载另一三个 类

1.new 另一三个 类的对象时new instance

2.调用类的静态措施(invoke static)

3.获取类的静态域的值(sget)

2.时要给应用开启新的守护程序不用 进行合并,而且很容易而且内存消耗等原因分析合并失败。

AndFix:由补丁类的classLoader加载补丁类,在native层针对不同Android架构中的不同的ArtMethod型态调用对应的replaceMethod措施按照定义好的ArtMethod型态一一替换措施的所有信息如所属类、访问权限、代码内存地址等。

稳定性较差,会受到国内ROM厂商对ArtMethod型态更改的影响,统统这正是AndFix不支持统统机型的原因分析。

再来看看腾讯系三大类加载方案的实现原理。QQ空间方案会侵入打包流程,而且为了hack打上去统统无用的信息,实现起来很不优雅。而QFix的方案,时要获取底层虚拟机的函数,缺陷稳定可靠,而且有个比较大的问題图片是无法新增public函数。

组织组织结构类而且有组织组织结构类把所有的field/method的private访问权限改成proteced而且public组织组织结构类将所有的field/method的private访问权限改成proteced而且public。

亲们采用的是例如类修复反射注入措施。把补丁so库的路径插入到nativeLibraryDirectories数组的最前面,就不用 达到加载so库的完后 是补丁so库,而不会 没人 so库的目录,从而达到修复的目的。

3.合并时占用额外磁盘空间,对于多DEX的应用来说,而且修改了多个DEX文件,就时要整理多个patch.dex与对应的classes.dex进行合并操作时这种情况会更严重,而且合并过程的失败率也会更高。

Java字节码中不蕴藏泛型类型信息,让你区别类型定义都可不可以 限定泛型类型

Lamda表达式具有函数式编程的特点,是Java中最接近闭包的概念。函数式接口:另一三个 接口具有唯另一三个 抽象措施

Java中的Runable和Comparator不会 典型的函数式接口

不过HotFix不会 缺陷:

1.不支持新增字段,以及修改措施,统统 支持对资源的替换。

阿里的HotFix方案,相对于QQ空间超级补丁技术和微信Tinker来说,定居于紧急BUG修复的场景下,不用 最及时的修复BUG,下拉补丁立即生效不用等待英文。

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)获取这种静态值。

组织组织结构类会被编译器生成同组织组织结构类一样的顶级类。只不过非静态组织组织结构类会持有组织组织结构类的引用。这也是Android性能优化建议Handler使用静态组织组织结构类,出理 组织组织结构类Activity只有被回收原因分析造成OOM。

2、找到所有完后 引用到原AssetManager的地方,通过反射,把引用处替换为AssetManager



从图中都可不可以 看完,亲们重新编排了包中dex的顺序。没人 ,在虚拟机查找类的完后 ,会优先找到classes.dex中的类,而且才是classes2.dex、classes3.dex,也都可不可以 看做是dex文件级别的类插桩方案。这种措施十分巧妙,它对旧包与补丁包中classes.dex的顺序进行了打破与重组,最终使得系统都可不可以 自然地识别到这种顺序,以实现类覆盖的目的。这而且大大减少合成补丁的开销。



动态注册的native措施映射通过加载so库过程中调用JNI_OnLoad措施调用完成,静态注册的native措施映射是在该native措施第一次执行的完后 才完成映射,当然前提是该so库而且load过。

而且字段居于了增加和减少,和措施变化的情况一样,所有字段的索引不会 居于变化。而且更严重的问題图片是,而且在守护程序上面某个类突然增加了另一三个 字段,没人对于没人 而且产生的这种类的实例,它们还是没人 的型态,这是无法改变的。而新措施使用到那些老的实例对象时,访问新增字段就会产生不可预期的结果。

组织组织结构类&number。number即编译器根据匿名组织组织结构类突然总出 在组织组织结构类中的顺序,依次累加。

新增/减少匿名组织组织结构类对热部署是无解的,而且补丁修复工具拿到的是class文件,无法区别DexFileDemo&1和DexFileDemo&2,会原因分析类的顺序乱套。而且匿名组织组织结构类插入到末尾则是允许。

在HostSpot VM下解释class文件的lamda表达式:

invokeDynamic指令调用java/lang/invoke/LamdaMetafactory的metafactory这种静态措施。这种措施会在运行时生成实现函数式接口的具体类,这种具体类会调用那个静态私有措施。

在Android虚拟机下解释dex文件中的lamda表达式:则是在优化成dex文件的完后 就生成了这种具体类。

我我人太好,dex比较的最佳粒度,应该是在类的维度。它既不像措施和指令维度那样的细微,统统 像bsbiff比较那般的粗糙。在类的维度,都可不可以 达到时间和空间平衡的最佳效果。基于这种准则,亲们另辟蹊径,实现了三种 完全不同的全量dex替换方案。

AndFix不同于QQ空间超级补丁技术和微信Tinker通过增加或替换整个DEX的方案,提供了三种 运行时在Native修改Filed指针的措施,实现措施的替换,达到即时生效不用重启,对应用无性能消耗的目的。其原理如下:



对于实现措施的替换,时要在Native层操作,主要经过另一三个 步骤:

组织组织结构类和组织组织结构类互相访问private措施和字段时,会自动在对应类为对方生成public的access&**措施。

不过细心的读者会发现,QQ空间超级补丁在使用 过程中还居于如下问題图片:

1.不支持即时生效,时要通过重启不用 生效。

微信的Tinker方案是完全的全量dex加载,而且可谓是将补丁合成做到了极致,然而亲们发现,精密的武器无须适用于所有战场。Tinker的合成方案,是从dex的措施和指令维度进行全量合成,整个过程不会 买车人研发的。

1.与超级补丁技术一样,不支持即时生效,时要通过重启应用的措施不用 生效。

这种措施整理完全的包很占用空间。而像统统方案,是先进行对资源包做差量,在运行时合成完全包打上去载。没人 我我人太好减少包的体积,而且在运行时多了合成的操作,耗费了运行时间喝内存。合成后的包也是完全的包,仍旧会占磁盘空间。

Lamada表达式和匿名组织组织结构类的区别:

1.this关键字指包围Lamada表达式的类而不会 指向匿名组织组织结构类买车人

2.编译措施,Java编译器将Lamda表达式编译成类的私有措施,使用了Java7的invokedynamic动态绑定这种私有措施。而匿名组织组织结构类则是生成组织组织结构类&number的新类.编译器不会 在类下生成lamda$main$*{ }私有静态措施,这种措施实现了lamda表达式的逻辑,引用的变量不会 变成措施的参数。

微信的热修复的流程如图所示:

不支持对静态字段和静态代码块的修改,会原因分析热部署失败,只有冷启动生效。支持非静态字段和非静态代码块修改,热部署统统 将init构造函数作为普通的措施变更。

针对上面的问題图片,腾讯出了QFix方案。

在native层提前调用dvmResolveClass,是的在dvmResolve中调用dvmDexGetResolve不为null,也出理 了校验一致性的问題图片。

这种方案要求传递的在多dex情况下,referrer类时要跟patch类是同另一三个 dex。fromUnverifiedConstant时要为true。referrer时要提前加载。

这方案时要统统问題图片,在dexopt完后 绕过,而且dexopt会改变统统没人 的逻辑,统统odex层面的优化会写死字段和访问措施的偏移。这会造成很严重的BUG。

QQ空间超级补丁采用的插桩措施,入侵打包流程,单独放另一三个 帮助类在独立的dex中让统统类调用,阻止类在dexopt时被打伤CLASS_ISPREVERIFIED标记。其原理如下图:



加载补丁dex得到dexFile对象作为参数构建另一三个 Element对象插入到dexElement数组最前面。

Tinker提供差量包,整体替换dex的方案。将patch.dex与应用的class.dex合并生成另一三个 完全的dex,加载完全的dex得到dexFile对象为参数构建另一三个 Element对象替换dexElements数组。

官方multiDex没人补丁查询更新,下载补丁待下次启动时生效。

其流程都可不可以 总结为如下图所示:

热部署不支持field/method增加和删除和 clinit措施的修改,静态field的初始化和静态代码块会被编译在编译器合成的措施clinit中,非静态字段的初始化会被编译在编译器生成的init无参构造函数中,

代码修复有两大主要方案:三种 是阿里系的底层替换方案,另三种 是腾讯系的类加载方案。底层替换方案限制颇多,但时效性最好,加载轻快,立即见效。类加载方案时效性差,时要重新冷启动不用 见效,但修复范围广,限制少。

而亲们也对代码的底层替换原理重新进行了深入思考,从克服其限制和兼容性入手,以三种 更加优雅的替换思路,实现了即时生效的代码热修复。sophix实现的是三种 无视底层具体型态的替换措施,也统统 把没人 没人 的逐一替换:

而且父类补丁变成了增加了泛型则会增加Bridge措施,造成热部署失败。

将措施从void get(B t) 变成 B extends Number void get(B t)措施逻辑不用居于变化,而且措施的签名会居于变化,这种情况热修复没人意义,时要出理 这种情况的居于。