首先定义一个功能类,类里定义一个test方法
public class UserInfoManager {
public UserInfoManager(){
}
public void test(Context context) {
Toast.makeText(context,"我是"+UserInfoManager.class.getName()+"",Toast.LENGTH_LONG).show();
}
}
然后定义一个针对改功能使用的接口类
public class Manager {
private UserInfoManager userInfoManager;
public Manager() {
userInfoManager = new UserInfoManager();
}
public void test(Context context) {
if (userInfoManager != null) {
userInfoManager.test(context);
}
}
}
在Activity中的按钮点击事件中使用如下方法调用
manager = new Manager(); manager.test(this);
执行后可以看到如下弹框
![]()
假如这个是一个线上APP的一段逻辑,这一段业务逻辑有问题,我们现在需要更改他;热修复的要求是直接替换这个类,用户只需要更新自己需要的类就可以完成替换;在这个要求下,我们首先需要想到的是在这一段逻辑中加入可更新类对象的方法,利用反射可以这样实现
实现一个新类继承自UserInfoManager
public class UserInfoManagerFix extends UserInfoManager {
public UserInfoManagerFix(){
}
@Override
public void test(Context context) {
Toast.makeText(context,"我是"+UserInfoManagerFix.class.getName()+"",Toast.LENGTH_LONG).show();
}
}
然后更新按钮点击的实现方式,利用反射替换UserInfoManager为UserInfoManagerFix
manager = new Manager();
//替换补丁类
try {
//new一个补丁
userInfoManagerFix = new UserInfoManagerFix();
//获取需要替换的对象字段
Field mUserInfoManagerA = manager.getClass().getDeclaredField("userInfoManager");
mUserInfoManagerA.setAccessible(true);
//核心,替换类引用
mUserInfoManagerA.set(manager, userInfoManagerFix);
//再次执行方法
manager.test(this);
} catch (Exception e) {
e.printStackTrace();
}
执行后,可以看到如下

是不是已经执行的是补丁类的方法
我们需要的是将这个类打包成补丁进行网络下载后加载,我简单的将其打包成jar,选择Build->Make Project-,完成后在
app\build\intermediates\javac\debug\classes\包名\下找到类名.class文件,然后利用jar 命令转化成一个jar。
我转化后放在assets下,如图
然后更改下点击事件
manager = new Manager();
//复制补丁到指定路径,模拟网络下载
String hotFixLibPath = getExternalCacheDir().getAbsolutePath() + File.separator + "UserInfoManagerFix.jar";
try {
InputStream inputStream = getAssets().open("UserInfoManagerFix.jar");
File file = new File(hotFixLibPath);
file.deleteOnExit();
file.createNewFile();
FileOutputStream outputStream = new FileOutputStream(file);
int read = -1;
while ((read = inputStream.read()) > 0) {
outputStream.write(read);
}
outputStream.flush();
outputStream.close();
} catch (Exception e) {
}
//利用DexClassLoader加载补丁,然后替换
String hotFixOutPath = getExternalCacheDir().getAbsolutePath();
DexClassLoader classLoader = new DexClassLoader(hotFixLibPath, hotFixOutPath, null, getClassLoader());
try {
Class hotFixCls = classLoader.loadClass("com.example.hotfix.managerfix.UserInfoManagerFix");
Object hotFixObj = hotFixCls.newInstance();
//找到字段进行替换
Field mUserInfoManagerA = manager.getClass().getDeclaredField("userInfoManager");
mUserInfoManagerA.setAccessible(true);
mUserInfoManagerA.set(manager, hotFixObj);
//再次执行方法
manager.test(this);
} catch (Exception e) {
e.printStackTrace();
}
执行点击

可以看到执行的是jar包中的方法。整个jar包也就2Kb
![]()
如果真的有这么一个需求,那么更新2kb的jar包总比更新整个APP体验好的多。
现在基本实现了一个简单的,可以进行热修复目的小Demo。
插件化和虚拟化双开APP的原理基本也如上。其核心过程都是更改系统原有的一些流程,因此需要从安卓源码进行分析;并且如果需要兼容不同版本系统,还需要在针对不同代码写不用的hook过程。
本文深入剖析了安卓热修复技术的实现原理,通过实例展示了如何利用反射和类加载机制替换应用中的类,实现无需重新安装即可修复应用bug或更新功能。
282

被折叠的 条评论
为什么被折叠?



