前天在知乎看见这样一个问题 :Android Studio 3.5提供的”Apply Changes“是什么原理?有个回答了JVMTI ,但是答主并未给出代码,在github看见https://github.com/AndroidAdvanceWithGeektime/JVMTI_Sample 大神的这个项目,然后便有了这篇实践博客
0. 先来看看效果吧
1. 首先android p(9.0)以后开始支持JVMTI
所以尽量在9.0的官方模拟器上测试
2. 应用 jvmti原理计算object
代码地址:https://github.com/zjw-swun/JVMTI_Demo
这里再次感谢 https://github.com/AndroidAdvanceWithGeektime/JVMTI_Sample dodola大佬
首先要明白jvmti支持哪些功能,需要看jvmti.h
这个提供了哪些功能函数,其实早在java 1.6的时代就已经支持自定义JVMTI agent了,只不过android没有实现,这次在android studio 3.5”Apply Changes“ 特性中使用了,对应就让android p(9.0)开始支持这个特性,低版本是没有
这个特性的
android中没提供计算object大小的api,但是熟悉java的朋友应该知道Instrument 这类有getObjectSize
这个方法,这里android虽然也有同名类,但是两者功能不一样,要实现getObjectSize
这个方法,直到这次android p(9.0)以后开始支持JVMTI 才提供,这里getObjectSize方法也可以自行计算,计算规则参考https://www.jb51.net/article/59809.htm,继续看jvmti.h
,搜索关键词getObjectSize
,发现目标代码
jvmtiError GetObjectSize(jobject object,
jlong* size_ptr) {
return functions->GetObjectSize(this, object, size_ptr);
}
在大佬源库的基础上改造,native-lib.cpp
中添加GetObjectSize
功能的支持,详细可以参考git提交记录,以下做点简单介绍
extern "C" JNIEXPORT jlong JNICALL tempGetObjectSize(JNIEnv *env,
jclass clazz,
jobject obj) {
}
static JNINativeMethod methods[] = {
{"getObjectSize", "(Ljava/lang/Object;)J", reinterpret_cast<jlong *>(tempGetObjectSize)},
{"retransformClasses", "([Ljava/lang/Class;)V", reinterpret_cast<void *>(tempRetransformClasses)}
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
ALOGI("==============library load====================");
jclass clazz = env->FindClass("com/dodola/jvmtilib/JVMTIHelper");
env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]));
return JNI_VERSION_1_6;
}
在JNI_OnLoad中动态注册一个{"getObjectSize", "(Ljava/lang/Object;)J", reinterpret_cast<jlong *>(tempGetObjectSize)},
的占位函数,然后在JvmTINativeMethodBind
函数前添加以下语句
extern "C" JNIEXPORT jlong JNICALL getObjectSize(JNIEnv *env, jclass clazz, jobject obj) {
jlong size;
jvmtiError result = localJvmtiEnv->GetObjectSize(obj, &size);
ALOGI("==========getObjectSize %d=======", size);
if (result != JVMTI_ERROR_NONE) {
char *err;
localJvmtiEnv->GetErrorName(result, &err);
printf("Failure running GetObjectSize: %s\n", err);
localJvmtiEnv->Deallocate(reinterpret_cast<unsigned char *>(err));
return -1;
}
return size;
}
在JvmTINativeMethodBind
函数里面添加以下语句
jmethodID methodid2 = jni_env->GetStaticMethodID(clazz, "getObjectSize", "(Ljava/lang/Object;)J");
if (methodid2 == method) {
*new_address_ptr = reinterpret_cast<jlong *>(&getObjectSize);
}
这里就完成了加载jvmti.so之后就能调用getObjectSize
当然JVMTIHelper.java类别忘记添加 JNI native 函数
public static native long getObjectSize(Object obj);
然后效果就和上面效果图一样了
3. jvmti.h 还有很多功能函数有空再讲别的吧
总之jvmti.h
最值得期待的 RedefineClasses
和 RetransformClasses
这个是实现 Apply Changes 的关键
共同学习,写下你的评论
评论加载中...
作者其他优质文章