0
点赞
收藏
分享

微信扫一扫

Android开发:热修复 Tinker 原理分析,移动端跨平台开发工具

====================================================================

通过源码得知,.dex文件是通过BaseDexClassLoader类(ClassLoader的子类)进行加载的,这个类里面有个成员变量DexPathList对象,而这个对象中有个有个数组存放的是DexElement对象,也就是从文件中加载的.dex文件,切入点就在这里

对于项目来说,一般项目会分包(方法数大于64k,及大于65535个时候,google提供的分包策略),如果采用Java代码实现热修复,分包是肯定要做的,因为要保证主包没有bug,分包简单说就是打包的apk一般有多个.dex文件

如: classes.dex,classes2.dex 等等…

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

了解了这些信息大致思路就有了,我们需要修复后的.dex文件加载解析,然后插队旧的安装包装的.dex文件,做到插队的操作,相当于欺骗了Android系统,大致如下:

[]( )实现原理

================================================================

思路大概是,我们需要一个修复bug的.dex文件,插队到BaseDexClassLoader类下的DexPathList对象DexElement数组中,并且排序到最前面,让系统加载到我们修复后的.dex文件不会再加载有bug的dex文件,完成插队(插装),这里会有个类加载机制的知识,本文不做详细介绍,后面会单独写一篇总结~ 大致实现步骤如下:

Android开发:热修复 Tinker 原理分析,移动端跨平台开发工具

[]( )Demo实现

==================================================================

1、基础配置-主包配置

配置分包,配置分包的目的主要是打包做出来的apk会有多个.dex文件,实际项目应用中要保证主包不要有bug,demo中加载.dex文件的时候也排除了主包文件classes.dex,大致如下: 创建BaseApplication,BaseActivity,MainActivity放置在主包,其中MainActivity主要是为了分包占位,只做了点击跳转分包中SecondActivity的逻辑 app目录下的build.gradle开启分包支持,在androiddefaultConfig下增加配置,其中multiDex-config.txt是配置保留在主包内类文件

//开启分包

multiDexEnabled true

//分包的配置,将配置文件中的放置在主包

multiDexKeepFile file("multiDex-config.txt")

添加分包依赖:

//multidex分包依赖

implementation 'com.android.support:multidex:1.0.3'

Application开启分包:

public class BaseApplication extends MultiDexApplication {

@Override

public void onCreate() {

super.onCreate();

}

@Override

protected void attachBaseContext(Context base) {

super.attachBaseContext(base);

//安装分包配置

MultiDex.install(this);

}

}

2、分包配置

分包就创建了一个SecondActivity类做模拟异常和修复异常的入口,和一个Calculate模拟异常,做了10/0的操作,修复后为10/1

注:获取修复后的classes2.dex文件可以通过直接buildapk直接解压获取,或者用build-tools下的dx.bat执行命令获取

简单贴下SecondActivity,点击fix按钮后的代码:

private void update() {

//将下载的修复包,复制到私有目录,解压从.dex文件中取到对应的.class文件

//从sd卡取修复包

File sourceFile = new File(Environment.getExternalStorageDirectory(), Constants.DEX_NAME);

//目标文件

File targetFile = new File(getDir(Constants.DEX_DIR, Context.MODE_PRIVATE).getAbsolutePath() + File.separator + Constants.DEX_NAME);

if (targetFile.exists()) {

targetFile.delete();

Log.e("update","删除原有dex文件(已使用的)");

}

//将SD卡中的修复包copy到私有目录

FileUtils.copyFile(sourceFile,targetFile);

Log.e("update","copy完成");

FixDexUtils.loadDexFile(this);

}

3、FixModule

新建一个Module处理热修复的相关逻辑

Android开发:热修复 Tinker 原理分析,移动端跨平台开发工具

只有五个文件,核心文件代码在FixDexUtils,其它是工具类,还有个定义了几个常量 看下FixDexUtils中的代码

public class FixDexUtils {

//修复文件可能有多个

private static HashSet<File> loadedDex = new HashSet<>();

//不建议这么写,demo演示用

static {

loadedDex.clear();

}

public static void loadDexFile(Context context) {

/

Android开发:热修复 Tinker 原理分析,移动端跨平台开发工具

/获取私有目录

File fileDir = context.getDir(Constants.DEX_DIR, Context.MODE_PRIVATE);

//遍历筛选私有目录中的.dex文件

File[] listFiles = fileDir.listFiles();

for (int i = 0; i < listFiles.length; i++) {

//文件名以.dex结尾,且不是主包.dex文件

if (listFiles[i].getName().endsWith(Constants.DEX_SUFFIX) && !"classes.dex".equalsIgnoreCase(listFiles[i].getName())) {

loadedDex.add(listFiles[i]);

}

}

//创建自定义的类加载器

createDexClassLoader(context ,fileDir);

}

/**

  • @param context

  • @param fileDir

  • 创建自己的类加载器,加载私有目录的.dex文件,上面已经将修复包中的dex文件copy到私有目录

*/

private static void createDexClassLoader(Context context, File fileDir) {

//解压目录

String optimizedDir = fileDir.getAbsolutePath()+File.separator+"opt_dex";

File fileOpt = new File(optimizedDir);

if (!fileOpt.exists()) {

fileOpt.mkdirs();

}

最后

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。希望能够帮助到大家提升技术

Android开发:热修复 Tinker 原理分析,移动端跨平台开发工具

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

举报

相关推荐

0 条评论