Android 手写热修复dex实例详解
作者:one裴s 发布时间:2022-11-06 08:01:40
现有的热修复框架很多,尤以AndFix 和Tinker比较多
具体的实现方式和项目引用可以参考网络上的文章,今天就不谈,也不是主要目的
今天就来探讨,如何手写一个热修复的功能
对于简单的项目,不想集成其他修复框架的SDK,也不想用第三方平台,只是紧急修复一些bug 还是挺方便的
言归正传,如果一个或多个类出现bug,导致了崩溃或者数据显示异常,如果修复呢,如果熟悉jvm dalvik 类的加载机制,就会清楚的了解 ClassLoader的 双亲委托机制 就可以通过这个
什么是双亲委托机制
当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。 每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。
突破口来了,看1(如果已经加载则直接返回原来已经加载的类) 对于同一个类,如果先加载修复的类,当后续在加载未修复的类的时候,直接返回修复的类,这样bug不就解决了吗?
Nice ,多看源码和jvm 许多问题可以从framework和底层去解决
话不多说,提出了解决方法,下面着手去实现
public class InitActivity extends FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这里默认在SD卡根目录,实际开发过程中可以把dex文件放在服务器,在启动页下载后加载进来
//第二次进入的时候可以根据目录下是否已经下载过,处理,避免重新下载
//最后根据当前app版本下载不同的修复dex包 等等一系列处理
String dexFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/fix.dex";
DexFile dexFile = null;
try {
dexFile = DexFile.loadDex(dexFilePath, null, Context.MODE_PRIVATE);
} catch (IOException e) {
e.printStackTrace();
}
patchDex(dexFile);
startActivity(new Intent(this, MainActivity.class));
}
/**
* 修复过程,可以放在启动页,这样在等待的过程中,网络下载修复dex文件
*
* @param dexFile
*/
public void patchDex(DexFile dexFile) {
if (dexFile == null) return;
Enumeration<String> enumeration = dexFile.entries();
String className;
//遍历dexFile中的类
while (enumeration.hasMoreElements()) {
className = enumeration.nextElement();
//加载修复后的类,只能修复当前Activity后加载类(可以放入Application中执行)
dexFile.loadClass(className, getClassLoader());
}
}
}
方法很简单在启动页,或者Application中提前加载有bug的类
这里写的很简单,只是展示核心代码,实际开发过程中,dex包下载的网络请求,据当前app版本下载不同的修复dex,文件存在的时候可以在Application中先加载一次,启动页就不用加载,等等,一系列优化和判断处理,这里就不过多说明,具体一些处理看github上的代码
###ok 代码都了解了,这个 fix.dex
文件哪里来的呢 熟悉Android apk生成的小伙伴都知道了,跳过这个步骤,不懂的小伙伴继续往下看
上面的InitActivity
中startActivity(new Intent(this, MainActivity.class));
启动了一个MainActivity
看看我的MainActivity
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//0不能做被除数,这里会报ArithmeticException异常
Toast.makeText(this, "结果" + 10 / 0, Toast.LENGTH_LONG).show();
}
}
哎呀不小心,写了一个bug 0 咋能做除数呢,app已经上线了,这里必崩啊,咋办 不要急,按照以下步骤:
我们要修复这个类
MainActivity
,先把bug解决
Toast.makeText(this, "结果" + 10 / 2, Toast.LENGTH_LONG).show();
把修复类生成.class
文件(可以先run一次,之后在 build/intermediates/javac/debug/classes/com开的的文件夹,找到生成的class文件,也可以通过javac 命令行生成,也可以通过右边的gradle Task生成)
把修复类
.class
文件 打包成dex (其他.class
删除,只保留修复类) 打开cmd命令行,输入下面命令
D:\Android\sdk\build-tools\28.0.3\dx.bat --dex --output C:\Users\pei\Desktop\dx\fix.dex C:\Users\pei\Desktop\dx\
D:\Android\sdk
为自己sdk目录 28.0.3
为build-tools
版本,可以根据自己已经下载的版本更换 后面两个目录分别是生成.dex
文件目录,和.class
文件目录
切记 .class
文件的目录必须是包名一样的,我的目录是 C:\Users\pei\Desktop\dx\com\pei\test\MainActivity.class
,不然会报 class name does not match path
这样dx文件夹下就会生成fix.dex文件了,把fix.dex放进手机根目录试试吧
再次打开App,完美Toast 结果5,完美解决
总结
修复方法要在bug类之前执行
适合少量bug,太多bug影响性能
目前只能修复类,不能修复资源文件
目前只能适配单dex的项目,多dex的项目由于当前类和所有的引用类在同一个dex会 当前类被打上CLASS_ISPREVERIFIED标记,被打上这个标记的类不能引用其他dex中的类,否则就会报错 解决办法是在构造方法里引用一个单独的dex中的类,这样不符合规则就不会被标记了
来源:https://juejin.cn/post/7203989318271483960


猜你喜欢
- 今天一个读者问我关于Android通过调用Webservice实现天气预报这篇文章的源码下载后出现的错误Could not find cla
- 1. 数据构造索引2个文档到 hotel 索引中:PUT /hotel/_doc/1{ "title": &
- springboot开启一个监听线程执行任务public class StartApplicationListener implements
- 简介Microsoft官网关于 WindowChome 的介绍截取Microsoft文章的一段话:若要在保留其标准功能时自定义窗口,可以使用
- 本文实例总结了C#中split用法。分享给大家供大家参考,具体如下:以下是我转载的两个不同的人的,方便大家及自己查阅string s=&qu
- 目录引言什么是Span关于String的一段性能提升测试代码最终性能对比写在最后引言C# 是一门现代化的编程语言,与Java十分的相似。熟练
- Navigator 的 push 和 pop方法Navigator 导航器的 push 和 pop 方法可以携带参数在页面间传递,其他变形的
- 在分布式系统中,我们会需要 ID 生成器的组件,这个组件可以实现帮助我们生成顺序的或者带业务含义的 ID。目前有很多经典的 ID 生成方式,
- 整理文档,搜刮出一个Android通过HTTP协议实现断点续传下载的代码,稍微整理精简一下做下分享。FileDownloader.java&
- 目录1)在程序集中添加资源2)在程序集中查找资源这一篇单独拿出来分析这个程序集资源,为的就是不想让大家把程序集资源和exe程序强关联,因为程
- 1:什么是Socket所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信
- 前言:最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到
- 最近,由于公司项目中需要将系统内用户操作的所有日志进行转存备份,考虑到以后可能还需要还原,所以最后决定将日志数据备份到Excel中。 下面是
- C#连接本地.mdf文件:项目中右键点击,新增——数据——基于服务的数据库,项目下直接生成.mdf数据库文件,后台(数据库的写入用参数传递)
- 这篇文章主要介绍了Java方法参数传递机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可
- Java泛型是JDK 5引入的一个特性,它允许我们定义类和接口的时候使用参数类型,泛型在集合框架中被广泛使用。类型擦除是泛型中最让人困惑的部
- 最近接触到个新项目,发现它用了一个比较有意思的框架,可以说实现了我刚入行时候的梦想,所以这里马不停蹄的和大家分享下。在我刚开始工作接触的项目
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- 一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对
- 问题现象描述:在Activity中控制播放时,按返回键退出应用后,音乐可在后台继续播放。重新进入app,音乐无法停止,重新点击开始播放音乐,