名字有點繞口,大概意思是使用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);