热更新的实现方式
Dex的处理
其实就是把多个dex放进app的classloader之中,从而使得所有dex的类都能被找到,如果第一个类被找到后续的重复类就不会被加载,只要将bug的类打包成一个dex 通过反射查到该dex集合之前就可以替换后续的bug。
指针处理
在native 使用指针替换bug的方法
Bsdiff算法
通过旧的dex 和新的dex 生成一个差异 的dex在客户端通过旧的dex还原成新的dex
AndFix的使用
AndFix 是通过指针处理 ,后续会介绍 。
第一步 依赖
compile 'com.alipay.euler:andfix:0.3.1@aar'
第二步
记得加权限
public class AliFixApplication extends Application {
private PatchManager patchManager;
@Override
public void onCreate() {
super.onCreate();
patchManager = new PatchManager(this);
// 每次appversion变更 会删除所有的补丁,如果appversion 不变,补丁不会删除
String s = BuildConfig.VERSION_CODE + "";
Log.e("application", s);
patchManager.init(s);//current version
patchManager.loadPatch();
addPath();
}
private void addPath() {
//寻找自己目录下的补丁文件,具体项目根据网络请求做相应的处理
String path = Environment.getExternalStorageDirectory().getPath() + "/Alarms/";
File file1 = new File(path);
File[] files = file1.listFiles();
for (File file : files) {
try {
patchManager.addPatch(file.getPath());
} catch (IOException e) {
e.printStackTrace();
Log.e("e", e.getMessage());
}
}
}
第三步
将旧的apk 和新的apk 通过 AndFix 提供了一个补丁创建工具 apkpatch. 做后续处理
通过命令行来生成
apkpatch.bat -f bug.apk -t new.apk -o patch -k key.store -p
123456 -a Hot-fix -e 123456
具体含义
-f 新版本apk 即修复后的apk
-t 旧版本apk 即修复前的apk
-o 差分包即patch 补丁文件存放的文件夹当前为patch 文件夹
-k keystore
-p 使用密码当前是123456
-a alias
-e alias 对应的密码当前是123456
将生成 的文件放到手机对于的目录下加载就可以了
生成的xxx.apatch 是个解压包 里面也有一个dex文件
AndFix的原理
MethodReplace 是个注解,表明了方法名和类,他的目标也是一个方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodReplace {
String clazz();
String method();
}
private void fixClass(Class<?> clazz, ClassLoader classLoader) {
Method[] methods = clazz.getDeclaredMethods();
MethodReplace methodReplace;
String clz;
String meth;
for (Method method : methods) {
methodReplace = method.getAnnotation(MethodReplace.class);
if (methodReplace == null)
continue;
//通过注解获取进行替换,说明他的的工具帮我们自动加上注解
clz = methodReplace.clazz();
meth = methodReplace.method();
if (!isEmpty(clz) && !isEmpty(meth)) {
replaceMethod(classLoader, clz, meth, method);
}
}
}
//到了 AndFix.initTargetClass(clzz); AndFix.addReplaceMethod(src, method);是ntaive的方法看不了 就跑到被人的github下看下
private void replaceMethod(ClassLoader classLoader, String clz,
String meth, Method method) {
try {
String key = clz + "@" + classLoader.toString();
Class<?> clazz = mFixedClass.get(key);
if (clazz == null) {// class not load
Class<?> clzz = classLoader.loadClass(clz);
// initialize target class
clazz = AndFix.initTargetClass(clzz);
}
if (clazz != null) {// initialize class OK
mFixedClass.put(key, clazz);
Method src = clazz.getDeclaredMethod(meth,
method.getParameterTypes());
AndFix.addReplaceMethod(src, method);
}
} catch (Exception e) {
Log.e(TAG, "replaceMethod", e);
}
}
然后跑到https://github.com/alibaba/AndFix/blob/master/jni/art/art_method_replace_6_0.cpp AndFix的github地址
void replace_6_0(JNIEnv* env, jobject src, jobject dest) {
。。。。
// 把原方法的各种属性都改成补丁方法的
smeth->declaring_class_ = dmeth->declaring_class_;
smeth->dex_cache_resolved_types_ = dmeth->dex_cache_resolved_types_;
smeth->access_flags_ = dmeth->access_flags_;
smeth->dex_cache_resolved_methods_ = dmeth->dex_cache_resolved_methods_;
smeth->dex_code_item_offset_ = dmeth->dex_code_item_offset_;
smeth->method_index_ = dmeth->method_index_;
smeth->dex_method_index_ = dmeth->dex_method_index_;
// 把原方法的各种属性都改成补丁方法的
smeth->ptr_sized_fields_.entry_point_from_interpreter_ =
dmeth->ptr_sized_fields_.entry_point_from_interpreter_;
smeth->ptr_sized_fields_.entry_point_from_jni_ =
dmeth->ptr_sized_fields_.entry_point_from_jni_;
smeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_ =
dmeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_;
。。。。
}
别人的实现大致是这个样子的
个人总结
AndFix支持2.3-6.0
AndFix支持启动应用就可以做更新
缺点
跳过了类初始化,对于静态或者构造函数或者class.forname()的处理可能会有问题
不能实现类替换,资源替换等
对加载过的补丁文件要做名字修改 如果名字重叠 就不会再次加载
参考文章
http://blog.csdn.net/watermusicyes/article/details/50529461
http://blog.csdn.net/xxooyc/article/details/50317455
http://www.open-open.com/lib/view/open1453255144980.html
http://blog.csdn.net/u010299178/article/details/52031505