android 热修复阿里,Android热修复(阿里热修复)

一、概述

当一个产品上线后,如果出现了bug,传统的方式是,收集bug,修复,重新发布新版本。这样用户还得重新下载apk,在这介绍的是不用重新安装apk修复bug,这样的技术就称之为热修复技术。

二、热修复原理

3009ad14508d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

principle.png

至于更加具体的原理,请点击这里https://github.com/alibaba/AndFix

三、实现步骤

1、生成补丁包

假如我们收到了用户上传的崩溃信息,我们改完需要修复的Bug,这个时候就会有一个新的的apk我们就叫它为new.apk,原来的那个有Bug的apk你也有我们就叫它old.apk。这个时候我们就可以利用阿里github上面提供的工具生成一个xxxx.apatch包用于修复Bug。

1.命令是:apkpatch.bat -f -t -o -k -p -a -e

2.-f : 没有Bug的新版本apk

3.-t : 有bug的旧版本apk

4.-o : 生成的补丁文件所放的文件夹

5.-k : 签名打包密钥

6.-p : 签名打包密钥密码

7.-a : 签名密钥别名

8.-e : 签名别名密码(这样一般和密钥密码一致)

2、通过补丁包修复bug

怎么获取apatch包呢?我们肯定是请求接口获取下载我们的修复好的apatch包,下载下来之后我们就可以调用方法进行修复了,我们可以暂时放在本地测试一下,在进行这步之前添加依赖https://github.com/alibaba/AndFix,这些代码肯定之前就得写好:

public class BaseApplication extends Application {

// Patch管理类

public static PatchManager mPatchManager;

@Override

public void onCreate() {

super.onCreate();

// 捕捉崩溃信息

ExceptionCrashHandler.getInstance().init(this);

// Ali热修复

try {

mPatchManager = new PatchManager(this);

// 初始化patch版本

String pkName = this.getPackageName();

String versionName = getPackageManager().getPackageInfo(pkName, 0).versionName;

// 初始化版本名称

mPatchManager.init(versionName);

// 加载之前的patch

mPatchManager.loadPatch();

} catch (PackageManager.NameNotFoundException e) {

e.printStackTrace();

}

}

}

public class MainActivity extends BaseSkinActivity {

@Override

protected void initData() {

// 获取上次的崩溃信息

File crashFile = ExceptionCrashHandler.getInstance().getCrashFile();

// 上传到服务器,后面再说.......

}

@Override

protected void initView() {

}

@Override

protected void setContentView() {

setContentView(R.layout.activity_main);

}

@Override

protected void initTitle() {

}

@Onclick(R.id.test)

public void test(View view) {

// 没有修复之前会报异常闪退

Toast.makeText(this, Utils.test(), Toast.LENGTH_LONG).show();

}

@Onclick(R.id.ali_fix)

public void aliHotFix(View view) {

try {

// 测试 目前暂且放在本地

String patchFileString = Environment.getExternalStorageDirectory()+"/fix.apatch";

Log.e("TAG", patchFileString);

// 修复apatch,不需要重启可立即生效

BaseApplication.mPatchManager.addPatch(patchFileString);

Toast.makeText(this, "Bug修复成功", Toast.LENGTH_LONG).show();

} catch (Exception e) {

e.printStackTrace();

Toast.makeText(this, "Bug修复失败", Toast.LENGTH_LONG).show();

}

}

}

3、底层原理

把fix.aptach包解压反编译里面的dex文件

3009ad14508d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Paste_Image.png

可见它其实是把两个apk进行比较然后找出里面不一样的方法,然后打成一个xxx..aptach包其实就是一个压缩文件,我们究竟怎么去获取dex里面的所有方法呢?解压后其实里面还有一个很重要的文件META-INF\PATCH.MF文件:

Manifest-Version: 1.0

Patch-Name: new

Created-Time: 13 Feb 2017 08:19:23 GMT

From-File: new.apk

To-File: old.apk

Patch-Classes:

// 所有的类都会在这里,有多个就会显示多个,需要用java代码把他解析出来拿到这个内容......

com.example.administrator.exceptioncrashhandler.Utils_C

F

Created-By: 1.0 (ApkPatch)

BaseApplication.mPatchManager.addPatch(patchFileString)这里面的Java代码中修改过的代码被加上一个注解:

3009ad14508d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Paste_Image.png

底层的修复c代码实现:

static void replaceMethod(JNIEnv* env, jclass clazz, jobject src,

jobject dest) {

if (isArt) {

art_replaceMethod(env, src, dest);

} else {

dalvik_replaceMethod(env, src, dest);

}

}

由于Android4.4后才用的Art虚拟机,之前的系统都是Dalvik虚拟机,因此Natice层写了2个方法,对不同的系统做不同的处理方式,我就只贴Dalvik部分了都类似,只不过Art需要版本判断不同Api的版本有所不同。

extern jboolean __attribute__ ((visibility ("hidden"))) dalvik_setup(

JNIEnv* env, int apilevel) {

// libdvm.so 加载系统的so库,视频里我们再去看一张图

void* dvm_hand = dlopen("libdvm.so", RTLD_NOW);

if (dvm_hand) {

dvmDecodeIndirectRef_fnPtr = dvm_dlsym(dvm_hand,

apilevel > 10 ?

"_Z20dvmDecodeIndirectRefP6ThreadP8_jobject" :

"dvmDecodeIndirectRef");

if (!dvmDecodeIndirectRef_fnPtr) {

return JNI_FALSE;

}

dvmThreadSelf_fnPtr = dvm_dlsym(dvm_hand,

apilevel > 10 ? "_Z13dvmThreadSelfv" : "dvmThreadSelf");

if (!dvmThreadSelf_fnPtr) {

return JNI_FALSE;

}

jclass clazz = env->FindClass("java/lang/reflect/Method");

jClassMethod = env->GetMethodID(clazz, "getDeclaringClass",

"()Ljava/lang/Class;");

return JNI_TRUE;

} else {

return JNI_FALSE;

}

}

extern void __attribute__ ((visibility ("hidden"))) dalvik_replaceMethod(

JNIEnv* env, jobject src, jobject dest) {

jobject clazz = env->CallObjectMethod(dest, jClassMethod);

ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr(

dvmThreadSelf_fnPtr(), clazz);

clz->status = CLASS_INITIALIZED;

Method* meth = (Method*) env->FromReflectedMethod(src);

Method* target = (Method*) env->FromReflectedMethod(dest);

LOGD("dalvikMethod: %s", meth->name);

// meth->clazz = target->clazz;

meth->accessFlags |= ACC_PUBLIC;

meth->methodIndex = target->methodIndex;

meth->jniArgInfo = target->jniArgInfo;

meth->registersSize = target->registersSize;

meth->outsSize = target->outsSize;

meth->insSize = target->insSize;

meth->prototype = target->prototype;

meth->insns = target->insns;

// 准确的说不能算是修复只能说是指向已修复的方法

meth->nativeFunc = target->nativeFunc;

}

最后,使用该方法修复bug需要注意的几点:

1.每次生成之后一定要测试;

2.尽量的不要分包,不要分多个dex

3.混淆的时候,设计到NDK AndFix.java 不要混淆

4.生成包之后一般会加固什么的,这个时候生成的差分包,一定要在之前去生成。

5.既然是去修复方法,第一个不能增加成员变量,不能增加方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值