名字有点绕口,大概意思是使用JNI反射一个Kotlin类的成员,这个成员是自定义类,并且我要调用这个类的函数。
在JAVA中调用一个static函数比较直接的,这个在百度上有比较多的例子。
调用了JniHelper这个类的changeVoice静态函数
jclass clazz = jniEnv->FindClass("com/xiaomakj/voicechanger/utils/JniHelper");
if (clazz == NULL) {
LOGI("%s", "com/xiaomakj/voicechanger/utils/JniHelper");
return;
}
jmethodID id = jniEnv->GetStaticMethodID(clazz, "changeVoice", "(Ljava/lang/String;II)V");
if (id == NULL) {
LOGI("%s", "method reChangeVoice not found");
} else {
jstring newpath = jniEnv->NewStringUTF("/storage/emulated/0/com.xiaomakj.voicechanger/newVoice.wav");
//jstring thispath = stoJstring(jniEnv, path);
jniEnv->CallStaticVoidMethod(clazz, id, newpath, mode, save);
LOGI("%s", "env->CallStaticVoidMethod(clazz,id)");
}
在Kotlin中我们知道是没有static修饰符的,而是被companion object{} 函数体统一包裹的方法体
如下:
companion object {
public fun setPosition(time: Int) {
}
}
我们使用调用Java的static函数的方式就会出现如下JNI DETECTED ERROR错误,既JNI找不到该函数的定义
JNI DETECTED ERROR IN APPLICATION: can't call void XXX on instance of java.lang.Class
我们看看是什么原因
使用jadx-gui反编译发现其源码中生成了一个Companion的静态内部类
那么问题就比较好解决了:
我们只需要获取这个Companion静态成员,并调用其内部setPosition函数即可。
下面给出JIN的调用过程
#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"axe",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"axe",FORMAT,##__VA_ARGS__);
//获取OperationActivity和Companion的两个jclass 注意$Companion为内部类的意思
jclass OperationActivityClazz = jniEnv->FindClass(
"com/xiaomakj/voicechanger/mvvm/ui/activity/OperationActivity");
jclass CompanionClazz = jniEnv->FindClass(
"com/xiaomakj/voicechanger/mvvm/ui/activity/OperationActivity$Companion");
if (OperationActivityClazz == NULL) {
LOGI("%s", "method OperationActivityClazz not found");
return;
}
//获取Companion成员ID(注意 参数1:OperationActivityClazz )
jfieldID OperationActivity$CompanionID = jniEnv->GetStaticFieldID(OperationActivityClazz, "Companion", "com/xiaomakj/voicechanger/mvvm/ui/activity/OperationActivity$Companion");
//获取Companion成员(注意 参数1:OperationActivityClazz )
jobject Companion = jniEnv->GetStaticObjectField(OperationActivityClazz, OperationActivity$CompanionID);
//获取Companion成员setPosition的函数ID(注意 参数1:CompanionClazz)
jmethodID OperationActivityClazzId = jniEnv->GetMethodID(CompanionClazz, "setPosition", "(I)V");
if (OperationActivityClazzId == NULL) {
LOGI("%s", "method OperationActivityClazzId not found");
return;
}
//调用Companion成员setPosition的函数
jniEnv->CallVoidMethod(Companion, OperationActivityClazzId, pos);
//手动回收 避免内存泄露
jniEnv->DeleteLocalRef(OperationActivityClazz);
jniEnv->DeleteLocalRef(Companion);