# 本demo实现原理来自

https://github.com/dodola/HotFix

https://zhuanlan.zhihu.com/p/20308548

# Anti类功能,及其原理

如上图,A,B,C是三个class,它们在生成apk文件时,被打包入同一个dex文件中,当apk发布出去运行一段时间发现A类有个bug,现在使用上面链接中的修复方案修复bug。如文中所说,如果直接将A类重写,并使用运行时动态地将修复后的A类所在的dex文件加载并插入到加载链前,则A便可以修复。但是因为在优化的过程中,如果dex文件中的class都不依赖外部文件,则dex文件内部的class会被打一个CLASS_ISPREVERIFIED标签,这样当B类再使用修复后的A类时,因它在另一个dex文件中,则会报运行时错误。所以在B类中,添加一行代码使得它依赖另外一个dex中的Anti类,这样B就不会被打标签。值得注意的是,apk运行的真实环境中,因并不确定哪一(哪几)个类将来会出现问题,所以为了将来可以修复它(它们),上面的A,B,C类都应该被加上引用Anti的代码;另外,因为Anti所在的dex是在Application类的onCreate方法中被加载,所以Application类的onCreate方法前不该引用到Anti类。

# anti_dex.jar 和bug_dex.jar的制作

主要过程:java -> class -> dex

java -> class, 可以使用ide如AS,或者直接命令行使用jdk提供的编译工具

javac -source 1.7 -target 1.7 –cp . –d . <MAIN_CLASS>,该命令需要在包的同一级目录下执行,且MAIN_CLASS包含包路径名。

class -> dex, 可以使用dx工具,形如

dx --dex --output=classes_dex.jar  <CLASS_PATH>

# 测试代码编写

@Override
public void onCreate() {
super.onCreate();
File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "anti_dex.jar");
Utils.prepareDex(this.getApplicationContext(), dexPath, "anti_dex.jar");
HotFix.patch(this, dexPath.getAbsolutePath(), "hotfix.test.com.example.didi.applicationtesthotfix.Anti");
dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "bug_dex.jar");
Utils.prepareDex(getApplicationContext(), dexPath, "bug_dex.jar");
HotFix.patch(getApplicationContext(), dexPath.getAbsolutePath(), "hotfix.test.com.example.didi.applicationtesthotfix.BugClass"); try {
Class<?> clazz = Class.forName("hotfix.test.com.example.didi.applicationtesthotfix.Anti");
equalLoader(clazz);
Log.e("Ruby", "Anti 's classloader' hashCode " + clazz.getClassLoader().hashCode() + " , " + clazz.getClassLoader().getClass().getName());
Class<?> cla = Class.forName("hotfix.test.com.example.didi.applicationtesthotfix.BugClass");
equalLoader(cla);
Log.e("Ruby", "BugClass 's classloader' hashCode " + cla.getClassLoader().hashCode() + " , " + cla.getClassLoader().getClass().getName());
} catch (Exception e) {
}
}

# 剔除Anti的编译脚本

因为Anti.class不能被使用AS一起打包入dex文件中,所以,在编译后打包工程的class文件成dex时,需要先将Anti.class删除,然后借助AS已近编译后的class文件和打包后的资源文件包自己命令行打包成apk文件。

# 打包class文件生成dex 文件
dx=/Users/didi/Library/Android/sdk/build-tools/23.0.3/dx classes=build/intermediates/classes/release/
$dx --dex --output=classes.dex $classes # 打包res/ assets/ 文件成 resources-release.ap_ 文件 (AS已完成) # 打包 resources-release.ap_ 和 classes.dex文件成 Hotfix-unsign.apk
sdklib=/Users/didi/Library/Android/sdk/tools/lib/sdklib.jar
apk=com.android.sdklib.build.ApkBuilderMain
app/build/intermediates/res
resources=build/intermediates/res/resources-release.ap_
java -classpath $sdklib $apk Hotfix-unsign.apk -u -z $resources -f classes.dex # 签名
keystore=/Users/didi/Documents/DidiChuxing/driver/lulei.keystore
alia=lulei
psw=demodebug
jarsigner=/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/jarsigner
$jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore $keystore -storepass $psw -keypass $psw -signedjar Hotfix-sign.apk Hotfix-unsign.apk $alia

# 运行时,动态热修复不行的原因

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

    Class<?> clazz = findLoadedClass(className);

    if (clazz == null) {
ClassNotFoundException suppressed = null;
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
suppressed = e;
}
if (clazz == null) {
try {
clazz = findClass(className);
} catch (ClassNotFoundException e) {
e.addSuppressed(suppressed);
throw e;
}
}
}
return clazz;
}

从上面的ClassLoader源码中,可以看到,查找一个类时,先查看内存中该类是否加载,所以如果在加载修复后的类文件A前,已经使用了A类,则该方案就失败,所以该方案要在Application的onCreate方法中将A所在的dex预先load。

# “字节码修改”和 “编译依赖”方案的区别

原则上没有任何区别!

# google 分包方案,是什么鬼,分包策略是什么?

I  still don’t  know!

#  华为i7-ATH-AL00 android版本5.1.1 为什么不插入Anti代码也可以?

What? Fuck!!!

#  反思

虽然使用了自定义的DexClassLoader将修复后的class文件从文件中重新加载到内存中了,但是因为方案中是将DexClassLoader(的父类)的DexFile通过反射赋值给了PathClassLoader(的父类BaseClassLoader 的pathLists成员),所以查看修复后类的classLoader,仍然显示为系统的pathClassLoader。

demo 代码请参考 github地址

android 基于分包方案的修复的更多相关文章

  1. Android dex分包方案和热补丁原理

    一.分包的原因: 当一个app的功能越来越复杂,代码量越来越多,也许有一天便会突然遇到下列现象: 1. 生成的apk在2.3以前的机器无法安装,提示INSTALL_FAILED_DEXOPT 2. 方 ...

  2. [转]Android dex分包方案

    转载自:https://m.oschina.net/blog/308583 当一个app的功能越来越复杂,代码量越来越多,也许有一天便会突然遇到下列现象: 1. 生成的apk在2.3以前的机器无法安装 ...

  3. Android dex分包方案

    当一个app的功能越来越复杂,代码量越来越多,也许有一天便会突然遇到下列现象: 1. 生成的apk在2.3以前的机器无法安装,提示INSTALL_FAILED_DEXOPT 2. 方法数量过多,编译时 ...

  4. 《android基于andFix的热修复方案》思路篇

    1:需求背景 项目上线之后,发现BUG需要修复(比如安卓兼容性等测试难以发现的问题),频繁的更新影响用户体验 2:方案要求 静默下载,耗费流量少,打完补丁后立刻生效,不用重启apk 3:解决思路 3. ...

  5. 《android基于andFix的热修复方案》实战篇

    有篇文章说的比较简洁,大家可以参考下:AndFix使用说明 下面说说实际使用中遇到的问题 1:如何继承到gradle项目中 dependencies { compile 'com.alipay.eul ...

  6. Android分包方案multidex

    对于功能越来越复杂的app的两大问题 问题一:当项目越来越大,方法数超过65536,编译时会出错(为什么是65536,参考下面关于dexopt对方法id检索存储介绍),这个所说的方法数包含用到的框架, ...

  7. Android 基于Netty的消息推送方案之对象的传递(四)

    在上一篇文章中<Android 基于Netty的消息推送方案之字符串的接收和发送(三)>我们介绍了Netty的字符串传递,我们知道了Netty的消息传递都是基于流,通过ChannelBuf ...

  8. Android 基于Netty的消息推送方案之字符串的接收和发送(三)

    在上一篇文章中<Android 基于Netty的消息推送方案之概念和工作原理(二)> ,我们介绍过一些关于Netty的概念和工作原理的内容,今天我们先来介绍一个叫做ChannelBuffe ...

  9. Android 基于Netty的消息推送方案之概念和工作原理(二)

    上一篇文章中我讲述了关于消息推送的方案以及一个基于Netty实现的一个简单的Hello World,为了更好的理解Hello World中的代码,今天我来讲解一下关于Netty中一些概念和工作原理的内 ...

随机推荐

  1. (原创)Stanford Machine Learning (by Andrew NG) --- (week 10) Large Scale Machine Learning & Application Example

    本栏目来源于Andrew NG老师讲解的Machine Learning课程,主要介绍大规模机器学习以及其应用.包括随机梯度下降法.维批量梯度下降法.梯度下降法的收敛.在线学习.map reduce以 ...

  2. Java学习笔记(13)

    StringBuffer 增加 append(boolean b)    可以添加任意类型的数据到容器中 insert(int offset,boolean b)  指定插入的索引值,插入对应的内容  ...

  3. 138.括号序列(区间型DP)

    3657 括号序列  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 我们用以下规则定义一个合法的括号序列: ...

  4. HDU 5289 Assignment rmq

    Assignment 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5289 Description Tom owns a company and h ...

  5. Alpha冲刺(6/10)——追光的人

    1.队友信息 队员学号 队员博客 221600219 小墨 https://www.cnblogs.com/hengyumo/ 221600240 真·大能猫 https://www.cnblogs. ...

  6. 班尼特·A·麦克道尔 - 一个交易者的资金管理系统(2013年5月26日)

    <一个交易者的资金管理系统:如何确保利润并避免破产风险> 作 者:班尼特·A·麦克道尔 系 列:“引领时代”金融投资系列-世界交易经典译丛 出 版:万卷出版公司 字 数:155千字 阅读完 ...

  7. CentOS 6.9下KVM虚拟机网络Bridge(网桥)方式与NAT方式详解(转)

    摘要:KVM虚拟机网络配置的两种方式:NAT方式和Bridge方式.Bridge方式的配置原理和步骤.Bridge方式适用于服务器主机的虚拟化.NAT方式适用于桌面主机的虚拟化. NAT的网络结构图: ...

  8. Word中公式从单栏排版变为双栏排版后公式和编号错开了

    如上图公式(2),把自己做的共识从通栏复制到期刊的双栏里就变成这样了(先复制过来参考文献,再复制正文,那么参考文献没事),原来一直搞不懂,今天把它显示所有标记发现多了个制表符(我原来以为是行标记),鼠 ...

  9. java接入极光推送

    直接提供工具类, 调用时直接调用其 sendToRegistrationId() 方法 1 import cn.jiguang.common.resp.APIConnectionException; ...

  10. iOS: 使用故事板和xib设置按钮圆角方法

    使用storyboard如何设置圆角或边框? 通过storyboard的 运行时属性runtime attribute,可以对Button设置圆角或者边框 1.很多人都知道,通常设置一个 Button ...