Android热修复原理(android最新热修复)

先说热修复原理的结论,就是把补丁dex文件放在dexElements数组的前面位置,这样在加载类的时候,首先会找到补丁包中的dex文件,加载到类中之后就不会再搜索了,这样就不会再使用原apk文件中同名的类,从而达到修复的目的。
正常开发流程热修复开发流程热修复应用程序热修复是“打补丁”。一个app上线,如果发现重大bug,需要紧急修复。一般的做法是修复bug,然后重新打包,然后上线各种渠道。这种方法成本高,效率低。于是热修复技术应运而生。hot fix技术的一般做法是在应用启动时,主动去服务器检查是否有补丁包。如果有,下载下来,下次启动就生效,这样网上的应急bug就能快速解决。
Android中的热修复包括:代码修复资源修复so(动态链接)库修复
热修复的优势资源热修复即时运行资源热修复分为两步:
构造一个新的Assetmanager,通过反射调用addAssetPath,将这个完整的资源包添加到AssetManager中,得到一个拥有所有新资源的asset manager;找到之前引用原资产管理器的所有地方,通过反射将引用替换为新资源的资产管理器;Sophix的方案:构造一个包id为0x66的资源包,只包含变更后的资源项。只需将这个包直接添加到原来的AssetManager中。So库修复将补丁so库的路径插入nativeLibraryDirectories数组的前面,这样加载so库的时候就是补丁so库,而不是原来so库的目录。
代码修复主要有三种方案,即:
底层替换方案类加载方案即时运行方案类加载方案类加载方案基于Dex分包方案。什么是Dex分包方案?先说65536限制和LinearAlloc限制。6536限制随着应用程序功能越来越复杂,代码量越来越大,引入的库越来越多,编译时可能会提示以下异常:
com . Android . dex . dexinxoverflowexception 3360方法id不在[0,0xffff] 3360 65536这说明应用中引用的方法数量超过了最大值65536。造成这个问题的原因是系统的65536限制。65536限制的主要原因是DVM字节码的限制。DVM指令集的invoke-kind索引为16位,最多可以引用65535个方法。
LinearAlloc限制可能会在安装过程中提示INSTALL_FAILED_DEXOPT。原因是LinearAlloc极限。DVM中的LinearAlloc是一个固定的缓冲区,当方法的数量超过缓冲区的大小时,就会报错。为了解决65536限制和LinearAlloc限制,产生了Dex分包方案。Dex分包方案主要是在打包时将应用代码分成多个Dex,将应用启动时必须使用的类和这些类的直接引用类放在主Dex中,其他代码放在次Dex中。应用启动时先加载主Dex,应用启动后再动态加载次Dex,缓解了主Dex的65536限制和LinearAlloc限制。Dex分包方案主要有两种,分别是Google官方方案、Dex自动解包方案和动态加载方案。因为Dex分包方案不是本章的重点,这里就不做过多介绍了。让我们继续学习类加载方案。调用DexPathList的findClass的方法,如下所示。libcore/dal vik/src/main/Java/dal vik/system/dexpath list . Java
公共类findClass(String name,List suppressed){ for(Element Element : dex elements){//1 Class clazz=Element . find Class(name,definingContext,suppressed);//2 if (clazz!=null){ return clazz;} } if(dexElementsSuppressedExceptions!=null){ suppressed . add all(arrays . aslist(dexelementssuprsedexceptions));}返回null}元素内部封装了DexFile,用来加载DexFiles,所以每个dex文件对应一个元素。许多元素组成一个有序的元素数组dexElements。当你想找一个类的时候,你会在注释1遍历元素数组dexElements(相当于遍历DexFile数组),在注释2调用元素的findClass方法,dexfile的loadClassBinaryName方法会在它的方法内部被调用来找类。如果在元素(dex文件)中找到该类,则返回该类;如果没有,将在下一个元素中进行搜索。按照上面的搜索过程,我们将修改bug类Key.class,然后将Key.class封装到包含dex的Patch.jar中,放在元素数组dexElements的第一个元素中。这样会先找到Patch.dex中的Key.class替换之前的bug key.class,数组后面的dex文件中有bug的key.class不会按照ClassLoader的父委托模式加载。这就是类加载方案,如下图所示。
类加载方案需要重启应用程序,让类加载器重新加载新的类。为什么需要重启?这是因为类是不能卸载的,所以如果你想重新加载一个新的类,你需要重启应用程序。因此,使用类加载方案的热修复框架不能立即生效。尽管许多修补程序框架采用了类加载方案,但在具体的实现细节和步骤上还是有一些差异。比如QQ空间和女娲的超级补丁,就是通过把补丁包放在上面提到的元素数组的第一个元素中来优先加载的。Tinker区分新旧apk得到patch.dex,然后将patch.dex与手机中apk的classes.dex合并生成新的classes.dex,然后在运行时通过反射将classes.dex放在元素数组的第一个元素中。如果饿了,Amigo把补丁包里每个dex对应的元素拿出来,然后组成一个新的元素数组。在运行时,它通过反射用新元素数组替换现有元素数组。类加载方案主要被腾讯采用,包括微信的Tinker、QQ空间的超级补丁、手机QQ的QFix、Amigo、女娲等。
底层替换方案和类加载方案的区别在于,底层替换方案不重新加载新类,而是直接在原生层修改原类。因为修改原类的限制太多,所以无法增减原类的方法和字段。如果我们增加方法的数量,方法的索引号也会增加,这样在访问方法的时候就无法通过索引找到正确的方法,同样的字段也差不多。底层替换方案与反射原理有关。以方法替换为例。我们可以调用Java . lang . class . getdeclaredmethod进行方法反射。假设我们要反映Key的show方法,我们会像下面这样调用它。
key . class . getdeclaredmethod(‘ show ‘)。invoke(key . class . new instance());Android 8.0的Invoke方法,如下图。libcore/ojluni/src/main/Java/Java/lang/reflect/method . Java
@FastNative公共本机对象调用(对象obj,对象.args)抛出IllegalAccessException、IllegalArgumentException、InvocationTargetExceptionInvoke方法是原生方法,对应Jni层的代码是:art/runtime/native/Java _ lang _ reflect _ method . cc。
静态job object Method _ invoke(JNIEnv * env,jobject javaMethod,jobject javaReceiver,job object Java args){ scopedfastnativeobjectaccesssoa(env);返回InvokeMethod(soa,javaMethod,javaReceiver,Java args);在Method_invoke函数中再次调用InvokeMethod函数:art/runtime/reflection.cc。
job object invoke method(const scopedobjectaccesssalreadyrunnable SOA,jobject javaMethod,jobject javaReceiver,jobject javaArgs,size_t num_frames) {.ObjPtr可执行文件=soa。decode(Java method);const bool accessible=executable-is accessible();art method * m=executable-GetArtMethod();//1.}注1在artvirtual machine中获取与传入Java方法(key的show方法)对应的ArtMethod指针。ArtMethod结构包含了Java方法的所有信息,包括执行入口、访问权限、所属的类和代码执行地址等。ArtMethod结构如下所示。艺术/运行时/艺术_方法. h
class ArtMethod FINAL {.protected : GC root declaring _ class _;STD : atomic access _ flags _;uint32 _ t dex _ code _ item _ offset _uint32 _ t dex _ method _ index _uint16 _ t method _ index _uint16 _ t hotness _ count _struct PtrSizedFields { art method * * dex _ cache _ resolved _ methods _;//1 void * data _;void * entry _ point _ from _ quick _ compiled _ code _;//2 } ptr _ size _ fields _;}ArtMethod结构中比较重要的字段是注1中的dex_cache_resolved_methods和注2中的entry _ point _ from _ quick _ compiled _ code,它们是方法的执行入口。当我们调用某个方法(比如Key的show方法)时,会得到show方法的执行入口,通过这个入口可以跳转执行show方法。替换ArtMethod结构中的字段或整个ArtMethod结构是底层替换方案。而Fix替换的是ArtMethod结构中的字段,所以会有兼容性问题,因为厂商可能会修改ArtMethod结构,导致方法替换失败。Sophix替换了整个ArtMethod结构,所以不会有兼容性问题。基础替代方法直接替换该方法,无需重新启动即可立即生效。底层置换方案主要是阿里系,包括AndFix、Dexposed、阿里百川、Sophix。
除了资源修复和代码修复,即时运行方案还可以借鉴即时运行的原理。可以说,即时运行的出现推动了热修复框架的发展。Instant Run在第一次构建apk时使用ASM将类似于以下内容的代码注入到每个方法中:
incremental change local incremental change=$ change;//1 if(localmincrementalchange!=null){//2 local incremental change . access $ dispatch(‘ onCreate。(land roid/OS/Bundle;)V ‘,新对象[] { this,param bundle });返回;} Note 1中有一个成员变量localIncrementalChange,值为$change,实现了抽象接口IncrementalChange。当我们单击InstantRun时,如果方法没有改变,并且$change为null,我们调用return而不做任何处理。如果方法改变,将生成替换类。这里,我们假设如果MainActivity的onCreate方法被修改,将生成替换类MainActivity$override。这个类实现了IncrementalChange接口,同时会生成一个AppPatchesLoaderImpl类。该类的getPatchedClasses方法将返回已修改类的列表(包含MainActivity)。根据列表,MainActivity的$change将被设置为MainActivity$override,因此满足了Note 2的条件,将执行MainActivity$override的access$dispatch方法。参考号’ o n C r e a t e .(L a n d r o I d/o s/B u n d L e;)V ‘根据参数’ onCreate ‘执行’ M A I in A C T I V I T Y Dispatch方法。(land roid/OS/Bundle;)v ‘ MainActivitydispatch方法的执行将基于参数’ onCreate。(land roid/OS/Bundle;)v ‘执行’ ‘ MainActivityoverride的onCreate方法,从而实现onCreate方法的修改。有借鉴即时运行原理的Robust和Aceso热修复框架。
【私信“手动”访问】安卓高级架构师核心技术笔记
在本文的最后,我们主要分析了热修复;资源修复,所以库修复。深入讲解代码修复中的三种方案。了解更多安卓技术,关注评论!

其他教程

4000万粉丝遍布社交媒体,《一禅小和尚》能否通过长剧和院线电影放大IP?

2022-8-28 2:44:01

其他教程

cad图纸资源网站(免费的cad素材网站)

2022-8-28 2:46:08

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索