安卓andFix热修复

2 篇文章 0 订阅

我们平常在项目中肯定有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了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值