0
点赞
收藏
分享

微信扫一扫

Android-Muitldex热更新修复方案原理,Java+性能优化+APP开发+NDK+跨平台技术

勇敢乌龟 2022-01-31 阅读 31

热更新技术,不是新话题。目前最热门的热更新由两种,一种是腾讯tinker为代表的 需重启app的热更新,一种是美团app为代表的instant Run,无需重启app. 今天先探究 前者的核心原理。

先看效果[github Demo地址] :(https://github.com/18598925736/HotUpdateDemo)
假如说这是我们的app界面,这个界面有个bug,我们直接用一个 TextView来表示

然而,我们的开发人员发现了这个bug,但是产品已经上线。这时候,由于引起bug的 代码,只有一行,

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceStata) {
super.onCreate(savedINstanceState);
srtContentView(R.layout.activity_main);

TextView textView = findViewById(R.id.tv);
Bug bug = new Bug():
String s = bug.getstr():
textView.setText(s):
}
}



这个时候,机智的程序员用最快的方式修复了这个bug,也只是改了一行代码:

那么,产品已经在线上,怎么办?我们通过后台,向app推送了一个 fix.dex文件, 等这个文件下载完成,app提示用户,发现新的更新,需要重启app. 待用户重启,代码修复 即会生效。无需发布新版本!

Demo使用方法

下载Demo代码之后,会在assets下看到一个fix.dex文件

按照正常的逻辑,我们做bug修复一定是把fix.dex放到服务器上, app去服务器下载它,然后存放在app私有目录,重启app之后,fix.dex生效, 当加载到这个类的时候,就会去读fix.dex中当时打包的已修复bug的类. 但是,我这里为了演示方便,直接放在assets,然后使用 项目中的 AssetsFileUtil类 用io流将它读写到 app私有目录下.

演示方法:

  1. 删掉 fix.dex ,运行app,你看到 手机屏幕中心 出现:“卧槽,有bug!”
  2. 还原 fix.dex ,运行app,你看到 手机屏幕中心 出现:“嘿嘿,bug已修复”

起作用的是谁?就是这个fix.dex文件.

Demo源码概览


如上图所示: 核心类其实就只有一个: ClassLoaderHookHelper ,它 就是 让 fix.dex这个补丁发挥作用的 " 幕后大佬". 这个核心类:有3个方法,分别是在不同的系统版本上,来对源码程序逻辑进行 hook,提高hook的兼容性.

下面是完整 ClassLoaderHookHelper代码 以及 使用它的 MyApp完整代码 :

import java.io.File;
import java.io.IOException;
import java.lang.reflec
t.Array;
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ClassLoaderHookHelper {

//23和19的差别,就是 makeXXXElements 方法名和参数要求不同
//后者是 makeDexElements(ArrayList files, File optimizedDirectory,ArrayList suppressedExceptions)
//前者是 makePathElements(List files, File optimizedDirectory,List suppressedExceptions)
public static void hookV23(ClassLoader classLoader,File outDexFilePath,File optimizedDirectory)throws IllegalAccessException, InvocationTargetException {
Field pathList =ReflectionUtil.getField(classLoader,“pathList”);//1、获DexPathList pathList 属性
object dexpathListobj =pathList.get(classLoader);//2、获DexPathList pathList对象
Field dexElementsField =ReflectionUtil.getField(dexPathListObj, “dexElements”);//3、获得DexPathList的dexElements属性

Object[] oldElements =(Object[]) dexElementsField.get(dexPathListObj);//4、获得pathList对象中 dexElements 的属性值

}
}

Multidex热修复核心技术

基础知识预备

1.Dex文件是什么?

我们写安卓,目前还是用 java比较多,就算是用 kotlin,它最终也是要转换成 java来运行。 java文件,被编译成 class之后,多个 class文件,会被打包成 classes.dex,被放到 apk中,安卓设备拿到 apk,去安装解析( 预编译balabala…),当我们运行 app时, app的程序逻辑全都是在classes.dex中。所以, dex文件是什么?一句话, dex文件是 android app的源代码的最终打包

2.Dex文件如何生成?

androidStudio 打包 apk的时候会生成 Dex,其实它使用的是 SDK的 dx命令,我们可以用 dx命令自己去打包想要打包的 class. 命令格式为:dx --dex --output=output.dex xxxx.class 将上面的output 和 xxxx换成你想要的文件名即可。

**注:**dx.bat在 安卓 SDK的目录下:比如我d的`C:\XXXXX\AndroidStudioAbout\sdk1\build-tools\28.0.3\dx.bat

3.ClassLoader是什么?

ClassLoader来自 jdk,翻译为 :类加载器,用于将 class文件中的类,加载到内存中,生成 class对象。只有存在了 Class对象,我们才可以创建我们想要的对象。 android SDK继承了JDKclassLoader,创造出了新的 ClassLoader子类。下图表示了 android9.0-28 所有的ClassLoader直接或者间接子类.

比较多的是 BaseDexClassLoaderDexClassLoader , PathClassLoader, 其他这些,应该是谷歌大佬 创造出来新的 类加载器子类吧,还没研究过。

注: 关于 DexClassLoaderPathClassLoader ,网上资料有个误区,应该不少人都认为, PathClassLoader 用于加载 app内部的 dex文件, DexClassLoader用于加载外部的 dex文件,但是其实只要看一眼 这两个类的关系,就会发现,它们都是继承自 BaseDexClassLoader,他们的构造函数内部都会去执行父类的构造函数。他们只有一个差别,那就是 PathClssLoader不用传 optimizedDirectory这个参数,但是 DexClassLoader必须传。这个参数的作用是,传入一个 dex优化之后的存放目录。而事实上,虽然 PathClassLoader不要求传这个 optimizedDirectory,但是它实际上是给了一个默认值。emmmm…所以不要再认为 PathClassLoader不能加载外部的 dex了,它只是没有让你传 optimizedDirectory而已。

另外: BootClassLoader用于加载 AndroidFramework层class文件( SDK中没有这个BootClassLoader,也是很奇怪) PathClassLoader 是用于Android应用程序类的加载器,可以加载指定的 dex,以及 jar、 zip、 apk中的 classes.dexDexClassLoader 可以加载指定的 dex,以及 jar、 zip、 apk中的 classes.dex
及 jar、 zip、 apk中的 classes.dexDexClassLoader 可以加载指定的 dex,以及 jar、 zip、 apk中的 classes.dex

举报

相关推荐

0 条评论