还不懂热修复的原理?看完这篇后带你手把手写代码

Android热修复原理(一)热修复框架对比和代码修复 相关文章 解析ClassLoader系列 前言 在Android应用开发中,热修复技术被越来越多的开发者所使用,也出现了很多热修复框架,比如:AndFix、Tinker、Dexposed和Nuwa等等。如果只是会这些热修复框架的使用那意义并不大,我们还需要了解它们的原理,这样不管热修复框架如何变化,只要基本原理不变,我们就可以很快的掌握它们。这一个系列不会对某些热修复框架源码进行解析,... 阅读详情

Android热修复原理分析

本文目的是让大家了解什么是热修复,具体的实现细节可以联系我。大家了解了原理后,才能够顺利的带着手写代码了。请大家记住一句话:
纸上得来终觉浅,绝知此事要躬行。
代码需要大家自己写出来的那才是自己的代码,否则的话你看了,背了,最后工作中还是写不出来优秀的代码

更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我联系我获取

什么是热修复

热修复: 让应用能够在无需重新安装的情况实现更新,帮助应用快速建立动态修复能力。

早期遇到Bug我们一般会紧急发布了一个版本。然而这个Bug可能就是简简单单的一行代码,为了这一行代码,进行全量或者增量更新迭代一个版本,未免有点大材小用了。而且新版本的普及需要时间,以Android用户的升级习惯,即使是相对活跃的微信也需要10天以上的时间去覆盖50%的用户。使用热修复技术,能做到1天覆盖70%以上。这也是基于补丁体积较小,可以直接使用移动网络下载更新。

热修复开发流程:

目前Android业内,热修复技术百花齐放,各大厂都推出了自己的热修复方案,使用的技术方案也各有所异。其中QZone超级补丁基于的是dex分包方案,而dex分包是基于Java的类加载机制 ClassLoader。

ClassLoader介绍

任何一个 Java 程序都是由一个或多个 class 文件组成,在程序运行时,需要将 class 文件加载到虚拟机 中才可以使用,负责加载这些 class 文件的就是 Java 的类加载机制。 ClassLoader 的作用简单来说就是加载 class 文件,提供给程序运行时使用。每个 Class 对象的内部都有一个 classLoader字段来标识自己是由哪个 ClassLoader加载的。

class Class<T> {
  ...
  private transient ClassLoader classLoader;
  ...
}

ClassLoader是一个抽象类,而它的主要实现类主要有:

  • BootClassLoader
    用于加载Android Framework层class文件
  • PathClassLoader
    用于Android应用程序类加载器。可以加载指定的dex,以及jar、zip、apk中的classes.dex
  • DexClassLoader
    用于加载指定的dex,以及jar、zip、apk中的classes.dex

很多博客里说 PathClassLoader只能加载已安装的apk的dex,但是实际上 PathClassLoader和 DexClassLoader一样都能够加载sdcard中的dex。

Log.e(TAG, "Activity.class 由:" + Activity.class.getClassLoader() +" 加载");
Log.e(TAG, "MainActivity.class 由:" + MainActivity.class.getClassLoader() +" 加载");

//输出:
Activity.class 由:java.lang.BootClassLoader@d3052a9 加载
MainActivity.class 由:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.enjoy.enjoyfix-1/base.apk"],nativeLibraryDirectories[/data/app/com.enjoy.enoyfix-1/lib/x86, /system/lib, /vendor/lib]]] 加载

它们之间的关系如下:

PathClassLoader与 DexClassLoader的共同父类是 BaseDexClassLoader

public class DexClassLoader extends BaseDexClassLoader {
   public DexClassLoader(String dexPath,String optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory),librarySearchPath, parent);
    }
}
public class PathClassLoader extends BaseDexClassLoader {
     public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent){
         super(dexPath, null, librarySearchPath, parent);
    }
}

可以看到两者唯一的区别在于: 创建 DexClassLoader需要传递一个 optimizedDirectory参数,并且会将其创建为 File对象传给 super,而 PathClassLoader则直接给到null。因此两者都可以加载指定的dex,以及jar、zip、apk中的classes.dex

PathClassLoader pathClassLoader = new PathClassLoader("/sdcard/xx.dex", getClassLoader());

File dexOutputDir = context.getCodeCacheDir();
DexClassLoader dexClassLoader= new DexClassLoader("/sdcard/xx.dex",dexOutputDir.getAbsolutePath(), null,getClassLoader());

optimizedDirectory参数为odex的目录。实际上Android中的ClassLoader在加载dex时,会首先经过dexopt对dex执行优化,产生odex文件。optimizedDirectory为null时的默认路径为:/data/dalvik-cache。并且处于安全考虑,此目录需要使用app私有目录,如:getCodeCacheDir()

在API 26源码中,将DexClassLoader的optimizedDirectory标记为了 deprecated 弃用,实现也变为了:

javapublicDexClassLoader(StringdexPath,StringoptimizedDirectory,StringlibrarySearchPath,ClassLoaderparent){super(dexPath,null,librarySearchPath,parent);}

和PathClassLoader一摸一样了!

双亲委托机制

创建 ClassLoader需要接收一个 ClassLoaderparent参数。这个 parent为父类加载。即:某个类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。这就是双亲委托机制!

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{

    // 检查class是否有被加载
    Class c = findLoadedClass(name);
    if (c == null) {
        long t0 = System.nanoTime();
        try {
            if (parent != null) {
                //如果parent不为null,则调用parent的loadClass行加载
                c = parent.loadClass(name, false);
            } else {
                //parent为null,则调用BootClassLoader进行加载
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {

        }

        if (c == null) {
            // 如果都找不到就自己查找
            long t1 = System.nanoTime();
            c = findClass(name);
        }
    }
    return c;
}

因此我们自己创建的ClassLoader: newPathClassLoader("/sdcard/xx.dex",getClassLoader());并不仅仅只能获得 xx.dex中的Class,还能够获得其父ClassLoader中加载的Class。

findClass

在所有父ClassLoader无法加载Class时,则会调用自己的 findClass方法。findClass在ClassLoader中的定义为:

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

其实任何ClassLoader子类,都可以重写 loadClass与 findClass。一般如果你不想使用双亲委托,则重写 loadClass修改其实现。而重写 findClass则表示在双亲委托下,父ClassLoader都找不到Class的情况下,定义自己如何去查找一个Class。而我们的 PathClassLoader会自己负责加载 MainActivity这样的程序中自己编写的类,利用双亲委托父ClassLoader加载Framework中的 Activity。说明 PathClassLoader并没有重写 loadClass,因此我们可以来看看PathClassLoader中的 findClass 是如何实现的。

public BaseDexClassLoader(String dexPath, FileoptimizedDirectory,String
                        librarySearchPath, ClassLoader parent) {
    super(parent);
    this.pathList = new DexPathList(this, dexPath, librarySearchPath,
                                    optimizedDirectory);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
    //查找指定的class
    Class c = pathList.findClass(name, suppressedExceptions);
    if (c == null) {
        ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" +                                                      name + "\" on path: " + pathList);
        for (Throwable t : suppressedExceptions) {
             cnfe.addSuppressed(t);
        }
            throw cnfe;
    }
    return c;
}

实现非常简单,从pathList中查找class。继续查看 DexPathList

public DexPathList(ClassLoader definingContext, String dexPath,
            String librarySearchPath, File optimizedDirectory) {
    //.........
    // splitDexPath 实现为返回 List<File>.add(dexPath)
    // makeDexElements 会去 List<File>.add(dexPath) 中使用DexFile加载dex文件返回 Element数组
    
this.dexElements =makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions, definingContext);
    //.........
}
public Class findClass(String name, List<Throwable> suppressed) {
     //从element中获得代表Dex的 DexFile
    for (Element element : dexElements) {
        DexFile dex = element.dexFile;
        if (dex != null) {
            //查找class
            Class clazz = dex.loadClassBinaryName(name,definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
    }
    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

热修复

PathClassLoader中存在一个Element数组,Element类中存在一个 dexFile成员表示dex文件,即:APK中有X个dex,则Element数组就有X个元素。

而对于类的查找,由代码for(Elementelement:dexElements)得知,会由数组从前往后进行查找。

在 PathClassLoader中的Element数组为:[patch.dex , classes.dex , classes2.dex]。如果存在Key.class位于patch.dex与classes2.dex中都存在一份,当进行类查找时,循环获得 dexElements中的 DexFile,查找到了Key.class则立即返回,不会再管后续的element中的 DexFile是否能加载到Key.class了。

因此,可以将出现Bug的class单独的制作一份patch.dex文件(补丁包),然后在程序启动时,从服务器下载patch.dex保存到某个路径,再通过patch.dex的文件路径,用其创建 Element对象,然后将这个 Element对象插入到我们程序的类加载器 PathClassLoader的 pathList中的 dexElements数组头部。这样在加载出现Bug的class时会优先加载patch.dex中的修复类,从而解决Bug。QQ空间热修复的原理就是这样,利用反射Hook了PathClassLoader中pathList的dexElements数组。

总结

在实现热修复的过程中,必须掌握的技术包括了反射、类加载机制并且掌握Framewrok层源码中关于 ClassLoader部分的内容。同时如果需要继续自动化补丁生成还需要掌握gradle编程等内容。

文章中每一个部分都包含一系列BAT面试的面试点,这些点构建了一个完整的知识体系,热修复。后面,我会细化里面的知识,如果 大家觉得有问题,请联系我~

请查看完整的PDF版

更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我联系我获取

Android学习——手把手教你实现Android热修复 前言 最近一段时间看了一些关于Android热修复的知识,比如Andfix,Tinker,Sophix等,看了这些框架的原理,就想着自己能不能手撸一个简单的demo。下面我们就来自己动手实现Android热修复吧。 热修复实现原理 所谓热修复就是,在我们应用上线后出现小bug需要及时修复时,不用再发新的安装包,只需要发布补丁包,在客户不知不觉之间修复掉bug,JAVA虚拟机JVM在运行时... 阅读详情

相关推荐

Android热修复原理热修复框架对比和代码修复

在Android应用开发中,热修复技术被越来越多的开发者所使用,也出现了很多热修复框架,比如:AndFix、Tinker、Dexposed和Nuwa等等。如果只是会这些热修复框架的使用那意义并不大,我们还需要了解它们的原理,这样不管热修复框架如何变化,只要基本原理不变,我们就可以很快的掌握它们。这一个系列不会对某些热修复框架源码进行解析,而是讲解热修复框架的通用原理。

2401_84121615的博客 915

热修复之代码修复

热修复主要有三种方案:1.底层替换 2.类加载 3.Instant Run方案 1.底层替换方案 主要利用反射原理,在反射用到的结构体ArtMethod,替换ArtMethod结构体,使其指向修复好的方法。 利用Native反射替换要修复的类的方法的信息(执行入口、访问权限、所属类、代码执行地址等)  即时生效,但由于基于Native层直接替换原有类,限制多,无法增减原有类的方法和字段 2...

xiangkezhu的博客 698

【信号与系统实战解析】—— 拉普拉斯变换在电路分析中的关键应用

本文深入解析了拉普拉斯变换在电路分析中的关键应用。通过将时域微分方程转换为复频域的代数方程,系统化处理初始条件,并利用零极点图分析电路稳定性与频率响应。文章以RLC串联电路为例,详细演示了s域建模、性质运用及综合分析方法,为工程师提供了分析动态电路的强大工具与清晰视角。

weixin_29093017的博客 174

Android全新技术

Android Jetpack:Jetpack是Android官方的一套库,旨在帮助开发者更快地构建高质量的Android应用程序,提供了许多常用的功能模块,例如数据存储、UI布局、网络通信等。Android应用束:Android应用束是一种新的应用程序打包格式,允许多个应用程序共享同样的应用程序组件,这有助于减少APK的大小,提高应用程序的性能。Android平台工具:这是一套新的公共工具,能够帮助Android开发者优化应用程序的性能和安全性,例如开发者调试工具、APK分析工具、性能剖析工具等。

m0_57138659的博客 1002

2021英伟达暑期实习面经(芯片设计)

2021英伟达暑期实习面经(芯片设计前端/DFT) 准备了将近一个月,终于赶在三月的最后一天拿到了offer,暑期实习算是上岸了,后面还有阿里/华为等一些国内公司的面试,但既然拿到了nv的offer,后面的先就当积累面试经验吧~ 网上关于nv的实习or秋招面经很少,硬件岗的更少(约等于没有好吧)。之前准备面试的时候,在网上找了半天,好像就只在牛客网上见过一篇关于nv硬件岗的面经,幸好当时认真看了哈哈。所以既然幸运上岸了,想趁自己还记得,record一下,希望可以帮到有缘人呀~ 好了,废话不多说,开始正题。

Hadleysjtu的博客 1万+

Python脚本自动化芯片设计?PyAether实战教程带你玩转EDA

本文通过PyAether实战教程,详细介绍了如何利用Python脚本实现芯片设计自动化。内容涵盖环境搭建、核心对象模型解析,并重点演示了原理图自动生成、参数化单元创建及设计验证流程集成,帮助EDA工程师从重复性手动操作中解放,显著提升设计效率与一致性。

e4f5g6h的博客 1312

2020 年需要关注的 5 大 Android 开发技术

作者 | Jake Lee译者 | 王强虽然编程环境每天都有新变化,但 Android 无疑是其中更新迭代最频繁的,每年甚至每个月都有新东西。本文介绍了 2020 年开发者最需要关注...

weixin_38754349的博客 370

京东云鼎环境配置与API安全接入实战:ISV服务商如何搞定数据加密与商家授权

本文详细介绍了京东云鼎环境配置与API安全接入的实战指南,帮助ISV服务商解决数据加密与商家授权等核心问题。通过京东开放平台的应用申请流程,结合多租户数据隔离策略和自动化授权实现,确保电商生态中的安全高效运营。文章还提供了避坑指南,助力开发者规避常见配置错误。

weixin_28705087的博客 352

2025年4月Android最热技术大盘点

Jetpack Compose已经逐渐取代XML布局,可能在2025年成为主流。跨平台开发方面,Flutter和鸿蒙系统的竞争会更加激烈。

weixin_37427992的博客 1875

SpringBoot 指标监控——Actuator

1、 访问:localhost:8000/actuator 设置健康监控显示具体情况 metrics http://localhost:8000/actuator/metrics/jvm.gc.live.data.size 定制化health信息 //利用构造器,在注册中心中添加cityService.saveCity.count指标 public CityServiceImpl(MeterRegistry meter...

weixin_44302046的博客 4627

如何在不牺牲精度的前提下,把 YOLOv11 压缩到 Jetson 能跑的程度

YOLOv11 在目标检测任务中具备较强的准确性与多尺度鲁棒性,但其原始模型参数规模与计算量,对边缘设备来说并不友好,特别是面向 Jetson 这类资源受限的推理平台。本文基于实际部署场景,逐步拆解从结构层级改造、剪枝策略制定、量化导出流程,到 Jetson TensorRT 推理落地全过程。在不牺牲主干精度的前提下,最终实现 YOLOv11 推理延迟缩短 3 倍、模型体积压缩 4 倍、mAP 损失控制在 1% 以内的轻量化优化结果,并总结出一条可复用的工程路径,供工业界与边缘侧模型部署开发者参考。

努力分享一些人工智能、计算机视觉、影像等相关的知识干货! 2425

劝学:Android 14 Framework 引入了哪些“新”技术栈,这下好了

Google 在 2017 年的 IO 大会上将 Kotlin 定义为 Google 官方支持的开发语言,时至今年,大部分 App 开发者都已经切过来了。然而,Android 框架开发者很多似乎还对这门语言嗤之以鼻,或者换句话说,平时用不到,再加上 Google 似乎一直没在框架里过多使用,所以暂时也没有学的必要。很”遗憾“,在 android 14 的 Settings 模块,我们开始看到了 Kotlin 的影子。自此,搞框架 App 开发的同学失去了最后一个不学 Kotlin 的借口。

老皮的博客 705

802.11be 的“上帝视角”:深入解析 CSI 技术原理与实现

IEEE 802.11be标准下的CSI技术解析 摘要:本文深入解析了802.11be标准中的信道状态信息(CSI)技术。CSI作为无线通信的"高精度地图",通过显式反馈机制获取信道矩阵H,经SVD分解得到预编码矩阵V。802.11be在CSI反馈机制上实现重大改进:支持320MHz带宽、16空间流;引入灵活的子载波分组(Ng=4/16)、更高量化精度(最高9/7比特)、部分带宽反馈和触发式反馈。这些创新显著提升了MU-MIMO和波束成形性能,为Wi-Fi 7带来极速通信、精准感知和Me

1042484520@qq.com(邮箱) 209

Android开发之热修复-Tinker-原理,这篇看完还不懂跟我去摆摊!

那么比如我们的中某个类的某个方法出现了异常,我们就可以创建一个修复包(修复后的文件),然后通过自定义的类加载器将修复后的文件copy到私有目录,再插队到的的dexElement的数组中,让系统优先加载文件,以做到热修复的目的,这种实现方式必须要执行修复逻辑后,重启app才能实现效果~

2401_89872639的博客 1041

图机器学习(9)——图正则化算法

网络信息能够有效约束模型行为,确保相邻节点的输出保持平滑过渡——这种特性在半监督场景中尤其重要,可实现未标注节点的信息传播。更进一步,这种机制也可用于正则化学习过程,从而提升模型在未见样本上的泛化能力。在本节中,我们探讨了在学习阶段应用正则化技术的方法,用于显著提升模型的鲁棒性,使其具备更优异的泛化能力。

盼小辉丶的博客 8903

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

切入源码层,Android开发之热修复-Tinker-原理,这篇看完还不懂跟我去摆摊!

考虑到文章的篇幅问题,我把这些问题和答案以及我多年面试所遇到的问题和一些面试资料做成了PDF文档《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!186)][外链图片转存中…(img-Ax151xTT-1712380235186)]《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

2401_84132544的博客 1020

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Android 手把手带你写热修复

热修复 通过Hook类的加载器,将我们的dex插入到dex元素数组的最前面达到热修复的目的,通常情况下类只会被加载一次 前言 随着公司的业务越来越复杂,代码迭代次数过多导致代码难以维护,很多潜在的逻辑关联容易被忽略,虽然在发版的时候有做灰度和ab,还是难以避免出现一些奇奇怪怪的bug或者机型适配问题。所以当务之急是接入热修复。本篇内容主要分析热修复原理,在文章最后会有一个demo对全文做一个概括。希望能对你有一些帮助 经测试发现android 10上可以完美运行 ClassLoader ClassLoade

u013894711的专栏 517

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Android开发之热修复-Tinker-原理,这篇看完还不懂跟我去摆摊!(1)

作为一名从事Android的开发者,很多人最近都在和我吐槽Android是不是快要凉了?而在我看来这正是市场成熟的表现,所有的市场都是温水煮青蛙,永远会淘汰掉不愿意学习改变,安于现状的那批人,希望所有的人能在大浪淘沙中留下来,因为对于市场的逐渐成熟,平凡并不是我们唯一的答案!网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化学习资料的朋友,可以戳这里获取一个人可以走的很快,但一群人才能走的更远!

2401_84520026的博客 1174

Android热修复原理及代码修复方案

在Android应用开发中,热修复技术被越来越多的开发者所使用,也出现了很多热修复框架,比如:AndFix、Tinker、Dexposed和Nuwa等等。如果只是会这些热修复框架的使用那意义并不大,我们还需要了解它们的原理,这样不管热修复框架如何变化,只要基本原理不变,我们就可以很快的掌握它们。这一个系列不会对某些热修复框架源码进行解析,而是讲解热修复框架的通用原理

z20131410的博客 1564

Android热修复实现及原理

今天我将围绕热修复实现原理以及常见的热修复方式来讲解热修复。如下图: #mermaid-svg-9DOgBI8Cs6Y6bRHs .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-9DOgBI8Cs6Y6bRHs .label text{fill:#333}#mermaid-svg-9DOgBI8Cs6Y6bRHs

hirezy 1万+
上一篇: 不知庐山真面目只缘身在此山中,讲解一下Handler
下一篇: 你告诉我太卡了,那是你不晓得性能优化之app卡顿优化
初壹十五a
博客等级 码龄8年 146粉丝 257原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值