我们平常在项目中肯定有bug,当刚上线就测出来个bug,怎么办呢,总不能再重新上线吧,这个时候就需要用到热修复了
DEMO地址:https://github.com/zhaopingfu/Lsn15_FixFramework
假如有个方法中报错了,工程直接挂掉了
public int caculator() {
int i = 0;
int j = 10;
//模拟异常产生
return j / i;
}
这个时候怎么办呢,作为程序员,肯定想第一时间解决这个bug,避免用户产生想卸载我们产品的想法
andFix的原理是将修复好的apk和有bug的apk进行比较,然后生成一个具有差异的dex文件,然后通过接口将dex下载到本地,当程序跑起来的时候,检测到有修复文件,就将dex加载进来,然后在底层将有错误的方法的的地址指向正确的方法的地址,当我们调用该方法的时候,因为在方法的地址已经指向了正确的方法,所以用户用起来就是没有问题的,也就达到了修复bug的目的。
extern "C" {
JNIEXPORT void JNICALL
Java_com_pf_fixframework_DexManager_replace(JNIEnv *env, jobject instance,
jint sdk,
jobject wrongMethod,
jobject rightMethod) {
//找到虚拟机对应的Method结构体
Method *wrong = (Method *) env->FromReflectedMethod(wrongMethod);
Method *right = (Method *) env->FromReflectedMethod(rightMethod);
//ClassObject
void *dvm_hand = dlopen("libdvm.so", RTLD_NOW);
findObject = (FindObject) dlsym(dvm_hand,
(sdk > 10 ? "_Z20dvmDecodeIndirectRefP6ThreadP8_jobject"
: "dvmDecodeIndirectRef"));
findThread = (FindThread) dlsym(dvm_hand, (sdk > 10 ? "_Z13dvmThreadSelfv" : "dvmThreadSelf"));
//method所声明的class
jclass methodClaz = env->FindClass("java/lang/reflect/Method");
jmethodID rightMethodId = env->GetMethodID(methodClaz, "getDeclaringClass",
"()Ljava/lang/Class;");
jobject ndkObject = env->CallObjectMethod(rightMethod, rightMethodId);
ClassObject *firstField = (ClassObject *) findObject(findThread(), ndkObject);
firstField->status = CLASS_INITIALIZED;
wrong->accessFlags |= ACC_PUBLIC;
wrong->methodIndex = right->methodIndex;
wrong->jniArgInfo = right->jniArgInfo;
wrong->registersSize = right->registersSize;
wrong->outsSize = right->outsSize;
//方法参数
wrong->prototype = right->prototype;
wrong->insns = right->insns;
wrong->nativeFunc = right->nativeFunc;
}
JNIEXPORT void JNICALL
Java_com_pf_fixframework_DexManager_replaceArt(JNIEnv *env, jobject instance,
jobject wrongMethod,
jobject rightMethod) {
//art虚拟机替换 art ArtMethod -->java方法
art::mirror::ArtMethod *wrong = (art::mirror::ArtMethod *) env->FromReflectedMethod(
wrongMethod);
art::mirror::ArtMethod *right = (art::mirror::ArtMethod *) env->FromReflectedMethod(
rightMethod);
// jclass jcls = env->FindClass("com/pf/fixframework/Patch");
// size_t firMid = (size_t) env->GetStaticMethodID(jcls, "f1", "()V");
// size_t secMid = (size_t) env->GetStaticMethodID(jcls, "f2", "()V");
//
// int size = secMid - firMid;
// memccpy(wrong, right, size, 0);
wrong->declaring_class_ = right->declaring_class_;
wrong->access_flags_ = right->access_flags_;
wrong->dex_code_item_offset_ = right->dex_code_item_offset_;
wrong->dex_method_index_ = right->dex_method_index_;
wrong->method_index_ = right->method_index_;
wrong->ptr_sized_fields_.entry_point_from_jni_ = right->ptr_sized_fields_.entry_point_from_jni_;
wrong->ptr_sized_fields_.entry_point_from_quick_compiled_code_ = right->ptr_sized_fields_.entry_point_from_quick_compiled_code_;
//#include "art_method.h"
// wrong->dex_cache_resolved_methods_ = right->dex_cache_resolved_methods_;
// wrong->dex_cache_resolved_types_ = right->dex_cache_resolved_types_;
// wrong->ptr_sized_fields_.entry_point_from_interpreter_ = right->ptr_sized_fields_.entry_point_from_interpreter_;
//7.0
wrong->hotness_count_ = right->hotness_count_;
wrong->ptr_sized_fields_.dex_cache_resolved_types_ = right->ptr_sized_fields_.dex_cache_resolved_types_;
wrong->ptr_sized_fields_.dex_cache_resolved_methods_ = right->ptr_sized_fields_.dex_cache_resolved_methods_;
}
}
这里因为5.0之后用的art虚拟机,之前是dalvik,他们在方法的底层结构体里面施有差异的,所以写了两个方法
这里下载了阿里巴巴的一个生成差异文件的工具,我在里面写了下生成差异文件的命令
地址:https://github.com/zhaopingfu/Lsn15_FixFramework/tree/master/tools
首先打一个release包(有bug的,通常是在线上的那个),再打一个修复好的包,然后打开cmd进入上面那个工具的路径下执行
C:\Users\Administrator\Desktop\apkpatch-1.0.3>apkpatch.bat -f new.apk -t old.apk -o output -k andfix.jks -p 123456 -a zhaopf -e 123456
-f:修复好的apk
-t:有bug的apk
-o:输出的文件目录
-k:签名
-p:签名密码
-a:别名
-e:别名密码
执行完了之后会在output路径下生成一个.apatch文件,这就是我们想要的那个修复文件,其实它就是个dex文件,这个时候可以交给后台了,我在这里不弄接口了,直接放到手机的SD卡的根目录下,再次进来的时候先点击修复,在执行刚才的错误方法,这个时候就可以顺利执行而不会有之前的bug了