前言

在上一篇文章Android类加载机制讲解了类加载器、加载dex、查找class相关的内容,并且透漏了热修复的原理,还没有看过的同学建议先看上一篇再来学习本文。

热修复的几种方案

1、基于类加载机制

2、底层替换方法

3、instant run 方法

今天我们研究的是方案1,其他方案以及资源文件修复后面文章会说。

基于类加载的修复原理

1、首先Android的类加载也是基于双亲委托机制,一个类只会被加载一次。那么我可以把有bug的类打包成补丁dex,下发到手机中,然后在加载原来的bug类之前加载补丁dex中修复好的类,那以后就不会再加载原来的bug类了,这种思路就可以到达修复class文件bug的目的。

2、思路很明确了,打包补丁dex也很容易(具体操作后面会说),下发补丁到app也容易。那就只剩下一个问题了,如何让已经修复好的类先于有bug的类加载?

在上一篇文章,我们对查找class做了较为详细的分析。其实答案就包含在其中,这里再回顾一下。查找class是通过DexPathList来完成的,内部是DexPathList遍历Element数组,通过Element获取DexFile对象来加载Class文件。由于数组是有序的,如果2个dex文件中存在相同类名的class,那么类加载器就只会加载数组前面的dex中的class。如果apk中出现了有bug的class,那只要把修复的class打包成dex文件并且放在DexPathList中Element数组的前面,就可以实现bug修复了。

热修复--类加载.png

关键步骤:

1、要获取加载apk的dex和布丁dex的类加载器PathClassLoader、DexClassLoader;

2、要获取Elements[]数组,只能通过反射BaseDexClassLoader、DexPathList这2个类去获取;

3、获取到2个dex文件的Element数组之后,需要创建一个新的数组把这2个数组合并,补丁dex的数组放在前面,原apk中放在后面;

4、再通过反射找去替换掉原来的dexElements属性即可。

类加载修复的实现

以下是工具类中的核心代码,需要完整代码的同学去文末git地址获取

/**

* 尝试加载app私有目录中的补丁dex去修复bug

* @param context

*/

fun loadPatch(context: Context) {

//这个是补丁dex存放的位置

dexPath = context.filesDir.path + "/patch0.dex"

if (!File(dexPath).exists())

return

//注意,只能用app私有目录去存放优化后的dex文件,如果放在外部存储存在注入攻击的风险

// data/data/包名/files/opt_dex

//在8.0及这个参数没有作用,只能用系统指定位置

optPath = context.filesDir.path + "/opt_dex"

var optFile = File(optPath)

if (!optFile.exists()) {

optFile.mkdirs()

}

//第一步,获取/创建apk和补丁dex的类加载器

var pathClassLoader: PathClassLoader = context.classLoader as PathClassLoader

var dexClassLoader = DexClassLoader(dexPath, optPath, null, pathClassLoader)

//第二步,反射获取BaseDexClassLoader中的DexPathList pathList属性

var pathPathList = getPathList(pathClassLoader)

var dexPathList = getPathList(dexClassLoader)

//第三步,反射获取获取DexPathList中的Element[] dexElements数组;

var pathElements = getDexElements(pathPathList)

var dexElements = getDexElements(dexPathList)

//第四步,合并Elements数组,注意补丁dex的数组要放在前面

var combineElements = combineArray(dexElements, pathElements)

//第五步,重新给PathClassLoader中的Element[] dexElements赋值(其实在PathList里面)

var pathList = getPathList(pathClassLoader) //再次获取apk中的PathList对象

setField(pathList, pathList.javaClass, DEX_ELEMENTS_FIELD, combineElements)

}

加载补丁dex

class MyApplication : Application() {

override fun onCreate() {

super.onCreate()

Log.e("tag", "Application onCreate: ")

loadPatch()

}

/**

* 1、这里测试是直接把补丁dex放在data/data目录,实际开发过程中可以把dex文件放在服务器,

* 在特定时机通过网络下载到app指定目录,然后在Application加载补丁dex

*

* 2、第二次进入的时候可以根据目录下是否已经下载过,处理,避免重新下载

*

*/

private fun loadPatch() {

// var dexFilePath: String = filesDir.path + "/classes.dex"

HotFixEngine.loadPatch(this)

var intent = Intent(this, MainActivity::class.java)

startActivity(intent)

}

}

加载补丁需要注意的2点,在注释已经标注了,主要是加载时机和避免重复加载。

注意,我这里是手动把补丁dex放在特定目录,我的目录是data/data/包名/files/patch0.dex,如果你要运行加载补丁的代码,你需要先把补丁dex放在特定目录,然后修改dexPath指向的路径才可以。

关于class文件打包dex,用android sdk tools自带的工具就可以了,具体操作可以参考: Android studio .class文件手动生成dex

参考

Android热修复更改图标,Android手写热修复(一)--ClassLoader相关推荐

  1. Android TensorFlow Lite 深度学习识别手写数字mnist demo

    一. TensorFlow Lite TensorFlow Lite介绍.jpeg TensorFlow Lite特性.jpeg TensorFlow Lite使用.jpeg TensorFlow L ...

  2. Android App实战项目之实现手写签名APP功能(附源码,简单易懂 可直接实用)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.跟踪滑动轨迹实现手写签名 手写签名的原理是把手机屏幕当作画板,把用户手指当作画笔,手指在屏幕上划来划去,屏幕就会显示手指的移动轨迹,就像画笔在 ...

  3. goodnote笔记同步 Android,GoodNotes 5 for Mac(智能手写笔记软件) +iCloud同步

    原标题:GoodNotes 5 for Mac(智能手写笔记软件) +iCloud同步 GoodNotes Mac版是 Mac 平台上的一款非常好用和实用的笔记软件.如果你在生活中有比较多的场景是需要 ...

  4. android按钮中添加图标,android 控件 带图标的按钮(ImageButton)

    1.继承关系和子类: 2.定义: mageButton就是用一个图标代表了一些文字的Button,它没Android:text属性.它由Android:src指定图标的位置 android:src=& ...

  5. android修改桌面app图标,android修改桌面app图标的问题。

    android修改桌面app图标的问题. 我知道配置app图标的做法是在 manifest.xml中的 android:allowBackup="true" android:ico ...

  6. Android 自定义注解详细用法,手写Butterknife黄油刀

    前言 本篇文章主要讲解 Java 注解在Android中的常见用法 Java 注解(Annotation) Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释 ...

  7. android通知栏语言更改,关于android语言切换后通知栏显示的问题

    之前在移动UIUE项目中发现一个问题: 改变android语言设置,但是状态栏的快捷功能显示文字不会立即发生改变. 同样,下拉通知栏的文字显示在切换语言后也不会同步更新. 于是在项目中引入: @Sdk ...

  8. android进度条的图标,Android进度条相关应用技巧解析

    Android手机操作系统中有很多功能在实际应用中体现了非常大的作用.比如在这里为大家介绍的Android进度条的实现,就可以帮助大家解决一些相应的问题.那么大家就一起来看看这方面的相关方法吧. ●进 ...

  9. android 获取默认程序图标,android – PackageManager.getApplicationIcon()返回默认图标?...

    我刚想通了.有一个PackageManager.getDefaultActivityIcon()方法返回一个Drawable.如果Drawable的Bitmap与应用程序图标Drawable的Bitm ...

最新文章

  1. 供给侧改革与去产能对安防产业啥影响
  2. PTA数据结构与算法题目集(中文)7-36
  3. 量子信息技术研究现状与未来
  4. zip unzip 命令
  5. django DateField需要前端传递的格式
  6. Eclipse搭建Android5.0应用开发环境 “ndk-build”:launchingfailed问题解决
  7. 本地远程连接阿里云Windows服务器并上传文件的方法
  8. 为了减少接口的响应时间,有哪些优化措施?(可以从架构、代码等各个角度谈)?
  9. java内存溢出前端_【面试笔录】内存溢出和内存泄漏
  10. 整理好的多款教程也素材与大家分享
  11. Golang环境windows 设置 GOROOT 和 GOPATH
  12. 快捷键你到底知道多少(Pr篇)
  13. 多线程——start()和run()
  14. tbc新服务器没消息,魔兽世界怀旧服,官方再流出TBC消息,舅舅党再次爆料燃烧的远征...
  15. Flutter和Native 通信 android端-pigeon
  16. 面试官最不喜欢不认同的5个跳槽理由
  17. 赴日研修的发展怎么样
  18. html飞机翼布局,基础知识 | 飞机客舱布局及主要设施介绍
  19. Unity如何画线条之美
  20. 光纤交换机 序列号_Brocade 光纤交换机常用命令

热门文章

  1. QtCreator格式化代码---Beautifier插件使用方式
  2. Ubuntu18.04构建Go语言项目
  3. 2021年中国制药机械市场趋势报告、技术动态创新及2027年市场预测
  4. Linux CentOS7 如何查看占用命令
  5. 【百度echarts】实现圆环进度条-代码示例代码demo
  6. php发布文章时 未定义索引,php – 上传文件时未定义的索引
  7. “龙书”作者斩获图灵奖!谷歌 AI 大神、Swift 之父都受它启蒙
  8. 机器闹乌龙?Amphetamine 险遭苹果下架
  9. 利用 Python 预测英雄联盟胜负,分析了 5 万多场比赛才得出的数据!
  10. 江湖有故人,欢迎来到程序员的江湖