上一篇使用了jvmti 完成了计算objecSize的功能,此次重点介绍一下如何实现android studio 3.5 apply change 功能,即如何在不重启应用的前提下运行时修改class,这里提一点android jvmti 是android p才开始提供的,而后只能运用于debugjava
out.dex中存放着修复以后的Test.class log输出为 修复的Test,而后最爽的一点是运行时修改class,无需重启哟~!!!android
代码地址 github.com/zjw-swun/JV… 喜欢就给个star吧,本代码fork自dodola大佬的github.com/AndroidAdva…,再次感谢 代码位置MainActivity中git
button_modify_class.setOnClickListener {
// redefineClass:对于已经加载的类从新进行转换处理,即会触发从新加载类定义,
// 须要注意的是,新加载的类不能修改旧有的类声明,譬如不能增长属性、不能修改方法声明
Test().log()
JVMTIHelper.init(this@MainActivity)
//dexbyte
val dexbyte = getBytes(assets.open("out.dex"))
JVMTIHelper.redefineClass(Test::class.java, dexbyte)
Test().log()
}
复制代码
重点就是这句话JVMTIHelper.redefineClass(Test::class.java, dexbyte)
调用了jni封装的jvmti的redefineClass方法github
native-lib.cpp
中bash
extern "C" JNIEXPORT jint JNICALL redefineClass(JNIEnv *env, jclass clazz, jclass target, jbyteArray dex_bytes) {
ALOGI("==========redefineClass =======");
jvmtiClassDefinition def;
def.klass = target;
def.class_byte_count = static_cast<jint>(env->GetArrayLength(dex_bytes));
signed char *redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
jvmtiError res = localJvmtiEnv->Allocate(def.class_byte_count,
const_cast<unsigned char **>(&def.class_bytes));
if (res != JVMTI_ERROR_NONE) {
return static_cast<jint>(res);
}
memcpy(const_cast<unsigned char *>(def.class_bytes), redef_bytes, def.class_byte_count);
env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
// Do the redefinition.
res = localJvmtiEnv->RedefineClasses(1, &def);
return static_cast<jint>(res);
}
复制代码
代码中已实现 MethoddEntry,对方法调用的hook能打印函数调用栈, 实现了获取classLoader加载的全部类信息app