如何解决 "Didn't find class "..." 在路径上:DexPathList 在我对 Java 的本机回调中
How to solve "Didn't find class "..." on path: DexPathList in my native callback to Java
我无法从 Android 应用程序的本机部分回调 Java(Kotlin)。这是一个音频应用程序,所以您会看到 "Audio a lot" 这个词,但我认为问题与此无关。
我已经阅读了很多文章并为此进行了几天的实验,但我无法解决我的问题。我可以从我的 native_lib.cpp
触发我的 java 回调,所以我已经测试了所有这些,我的 JniBridge
和我的本机代码之间的方法引用应该是正确的。
但是我想从一个单独的 class 来完成它,而且我还必须能够从不同的线程来完成它并且多次重复。所以我想尽可能永久地存储我对 JVM 回调 class 的引用,但我没弄对。
我当前的代码因以下错误而失败:
A: java_vm_ext.cc:542] JNI DETECTED ERROR IN APPLICATION: JNI GetMethodID called with pending exception java.lang.ClassNotFoundException: Didn't find class "com.my.app.common.jni.JniBridge" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /system/vendor/lib64, /system/lib64, /system/vendor/lib64]]
A: java_vm_ext.cc:542] at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:134)
A: java_vm_ext.cc:542] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
A: java_vm_ext.cc:542] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
A: java_vm_ext.cc:542]
A: java_vm_ext.cc:542] in call to GetMethodID
A: java_vm_ext.cc:542] "Thread-6" prio=10 tid=17 Runnable
A: java_vm_ext.cc:542] | group="main" sCount=0 dsCount=0 flags=0 obj=0x13080000 self=0x7ca400e800
A: java_vm_ext.cc:542] | sysTid=30175 nice=-16 cgrp=default sched=1073741825/2 handle=0x7c911014f0
A: java_vm_ext.cc:542] | state=R schedstat=( 5738847 149269 9 ) utm=0 stm=0 core=1 HZ=100
A: java_vm_ext.cc:542] | stack=0x7c91006000-0x7c91008000 stackSize=1009KB
A: java_vm_ext.cc:542] | held mutexes= "mutator lock"(shared held)
A: java_vm_ext.cc:542] native: #00 pc 00000000003cb654 /system/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+220)
以下是我构建的内容的描述:
我已经构建了这个回调 class:
AudioCallback.h:
#include <jni.h>
class AudioCallback {
public:
explicit AudioCallback(JavaVM&, jobject&);
void playBackProgress(int progressPercentage);
private:
JavaVM& mJvm;
jobject& mObject;
};
AudioCallback.cpp:
#include <jni.h>>
#include <utils/logging.h>
#include "AudioCallback.h"
AudioCallback::AudioCallback(JavaVM &jvm, jobject &object) : mJvm(jvm), mObject(object) {
}
void AudioCallback::playBackProgress(int progressPercentage) {
JNIEnv *g_env = NULL;
int getEnvStat = mJvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
JavaVMAttachArgs vmAttachArgs;
vmAttachArgs.version = JNI_VERSION_1_6;
vmAttachArgs.name = NULL;
vmAttachArgs.group = NULL;
if (getEnvStat == JNI_EDETACHED) {
LOGD("GetEnv: not attached - attaching");
if (mJvm.AttachCurrentThread(&g_env, &vmAttachArgs) != 0) {
LOGD("GetEnv: Failed to attach");
}
} else if (getEnvStat == JNI_OK) {
LOGD("GetEnv: JNI_OK");
} else if (getEnvStat == JNI_EVERSION) {
LOGD("GetEnv: version not supported");
}
if (g_env != NULL) {
jclass target = g_env->FindClass("com/my/app/common/jni/JniBridge");
jmethodID id = g_env->GetMethodID(target, "integerCallback", "(I)V");
g_env->CallVoidMethod(mObject, id, (jint) progressPercentage);
} else {
LOGE("JNIEnv is null!");
mJvm.DetachCurrentThread();
}
}
在我的 native_lib.cpp
中,我像这样在 JNI_OnLoad
中获取 JavaVM:
JavaVM *jvm;
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv jvm_env;
int getEnvStatus = vm->GetEnv((void **) &jvm_env, JNI_VERSION_1_6);
if (getEnvStatus != JNI_OK) {
LOGE("JNI_ONLOAD Failed to get the environment using GetEnv()");
return -1;
}
jvm = vm;
if (jvm == NULL) {
LOGE("JNI_ONLOAD: globabl jvm is NULL");
} else {
LOGD("JNI_ONLOAD: global jvm is NOT NULL");
}
LOGD("Onload done");
return JNI_VERSION_1_6;
}
之后,我有一个构建回调的方法和使用它的 AudioEngine
class:
JNIEXPORT void JNICALL
Java_com_my_app_common_jni_JniBridge_playFromJNI(JNIEnv *env, jobject instance,jstring URI) {
// Here I instantiate my callback
callback = std::make_unique<AudioCallback>(*jvm, instance);
// below code sets up my audio engine class, which calls the AudioCallback during playback.
// This works without problems.
const char *uri = env->GetStringUTFChars(URI, NULL);
AMediaExtractor *extractor = AMediaExtractor_new();
if (extractor == nullptr) {
LOGE("Could not obtain AMediaExtractor");
return;
}
media_status_t amresult = AMediaExtractor_setDataSource(extractor, uri);
if (amresult != AMEDIA_OK) {
LOGE("Error setting extractor data source, error %d", amresult);
}
audioEngine = std::make_unique<AudioEngine>(*extractor, *callback);
audioEngine->setFileName(uri);
audioEngine->start();
}
audioEngine
使用 oboe 库进行音频处理,所以我无法控制其中可能的线程创建,因此 AttachCurrentThread()
在我的回调 class.附加和分离工作,唯一失败的是 Java.
的回调
我主要是自己解决了这个问题(阅读@Michael 在评论中的建议以及那里提到的其他一些地方肯定有帮助!):
首先,我将 "setup" 部分与实际的回调函数分开(这一直是计划,但不这样做首先阻碍了看到问题的方式)。
其次,在我的 native_lib.cpp
中,我在传递之前创建了对 jobject
的全局引用(问题示例中 playFromJNI
中的 "instance" 对象)像这样进入 AudioCallback's
构造函数:
myJNIClass = env->NewGlobalRef(instance);
callback = std::make_unique<AudioCallback>(*g_jvm, myJNIClass);
第三,在实际的回调方法中 playbackProgress
我只在必要时处理 ÀttachToCurrentThread`,并使用我事先用正确数据初始化的 class 成员。
这就是我的 AudioCallback
实现现在的样子:
#include <jni.h>>
#include <utils/logging.h>
#include "AudioCallback.h"
jclass target;
jmethodID id;
AudioCallback::AudioCallback(JavaVM &jvm, jobject object) : g_jvm(jvm), g_object(object) {
JNIEnv *g_env;
int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
LOGD("Env Stat: %d", getEnvStat);
if (g_env != NULL) {
target = g_env->GetObjectClass(g_object);
id = g_env->GetMethodID(target, "integerCallback", "(I)V");
}
}
void AudioCallback::playBackProgress(int progressPercentage) {
JNIEnv *g_env;
int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
LOGD("GetEnv: not attached - attaching");
if (g_jvm.AttachCurrentThread(&g_env, NULL) != 0) {
LOGD("GetEnv: Failed to attach");
}
} else if (getEnvStat == JNI_OK) {
LOGD("GetEnv: JNI_OK");
} else if (getEnvStat == JNI_EVERSION) {
LOGD("GetEnv: version not supported");
}
g_env->CallVoidMethod(g_object, id, (jint) progressPercentage);
}
这现在可以按我的需要工作,但由于我不是 C++/JNI 专家,我的代码中可能仍然存在问题。请随时指出它们!
我无法从 Android 应用程序的本机部分回调 Java(Kotlin)。这是一个音频应用程序,所以您会看到 "Audio a lot" 这个词,但我认为问题与此无关。
我已经阅读了很多文章并为此进行了几天的实验,但我无法解决我的问题。我可以从我的 native_lib.cpp
触发我的 java 回调,所以我已经测试了所有这些,我的 JniBridge
和我的本机代码之间的方法引用应该是正确的。
但是我想从一个单独的 class 来完成它,而且我还必须能够从不同的线程来完成它并且多次重复。所以我想尽可能永久地存储我对 JVM 回调 class 的引用,但我没弄对。
我当前的代码因以下错误而失败:
A: java_vm_ext.cc:542] JNI DETECTED ERROR IN APPLICATION: JNI GetMethodID called with pending exception java.lang.ClassNotFoundException: Didn't find class "com.my.app.common.jni.JniBridge" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /system/vendor/lib64, /system/lib64, /system/vendor/lib64]]
A: java_vm_ext.cc:542] at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:134)
A: java_vm_ext.cc:542] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
A: java_vm_ext.cc:542] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
A: java_vm_ext.cc:542]
A: java_vm_ext.cc:542] in call to GetMethodID
A: java_vm_ext.cc:542] "Thread-6" prio=10 tid=17 Runnable
A: java_vm_ext.cc:542] | group="main" sCount=0 dsCount=0 flags=0 obj=0x13080000 self=0x7ca400e800
A: java_vm_ext.cc:542] | sysTid=30175 nice=-16 cgrp=default sched=1073741825/2 handle=0x7c911014f0
A: java_vm_ext.cc:542] | state=R schedstat=( 5738847 149269 9 ) utm=0 stm=0 core=1 HZ=100
A: java_vm_ext.cc:542] | stack=0x7c91006000-0x7c91008000 stackSize=1009KB
A: java_vm_ext.cc:542] | held mutexes= "mutator lock"(shared held)
A: java_vm_ext.cc:542] native: #00 pc 00000000003cb654 /system/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+220)
以下是我构建的内容的描述:
我已经构建了这个回调 class: AudioCallback.h:
#include <jni.h>
class AudioCallback {
public:
explicit AudioCallback(JavaVM&, jobject&);
void playBackProgress(int progressPercentage);
private:
JavaVM& mJvm;
jobject& mObject;
};
AudioCallback.cpp:
#include <jni.h>>
#include <utils/logging.h>
#include "AudioCallback.h"
AudioCallback::AudioCallback(JavaVM &jvm, jobject &object) : mJvm(jvm), mObject(object) {
}
void AudioCallback::playBackProgress(int progressPercentage) {
JNIEnv *g_env = NULL;
int getEnvStat = mJvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
JavaVMAttachArgs vmAttachArgs;
vmAttachArgs.version = JNI_VERSION_1_6;
vmAttachArgs.name = NULL;
vmAttachArgs.group = NULL;
if (getEnvStat == JNI_EDETACHED) {
LOGD("GetEnv: not attached - attaching");
if (mJvm.AttachCurrentThread(&g_env, &vmAttachArgs) != 0) {
LOGD("GetEnv: Failed to attach");
}
} else if (getEnvStat == JNI_OK) {
LOGD("GetEnv: JNI_OK");
} else if (getEnvStat == JNI_EVERSION) {
LOGD("GetEnv: version not supported");
}
if (g_env != NULL) {
jclass target = g_env->FindClass("com/my/app/common/jni/JniBridge");
jmethodID id = g_env->GetMethodID(target, "integerCallback", "(I)V");
g_env->CallVoidMethod(mObject, id, (jint) progressPercentage);
} else {
LOGE("JNIEnv is null!");
mJvm.DetachCurrentThread();
}
}
在我的 native_lib.cpp
中,我像这样在 JNI_OnLoad
中获取 JavaVM:
JavaVM *jvm;
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv jvm_env;
int getEnvStatus = vm->GetEnv((void **) &jvm_env, JNI_VERSION_1_6);
if (getEnvStatus != JNI_OK) {
LOGE("JNI_ONLOAD Failed to get the environment using GetEnv()");
return -1;
}
jvm = vm;
if (jvm == NULL) {
LOGE("JNI_ONLOAD: globabl jvm is NULL");
} else {
LOGD("JNI_ONLOAD: global jvm is NOT NULL");
}
LOGD("Onload done");
return JNI_VERSION_1_6;
}
之后,我有一个构建回调的方法和使用它的 AudioEngine
class:
JNIEXPORT void JNICALL
Java_com_my_app_common_jni_JniBridge_playFromJNI(JNIEnv *env, jobject instance,jstring URI) {
// Here I instantiate my callback
callback = std::make_unique<AudioCallback>(*jvm, instance);
// below code sets up my audio engine class, which calls the AudioCallback during playback.
// This works without problems.
const char *uri = env->GetStringUTFChars(URI, NULL);
AMediaExtractor *extractor = AMediaExtractor_new();
if (extractor == nullptr) {
LOGE("Could not obtain AMediaExtractor");
return;
}
media_status_t amresult = AMediaExtractor_setDataSource(extractor, uri);
if (amresult != AMEDIA_OK) {
LOGE("Error setting extractor data source, error %d", amresult);
}
audioEngine = std::make_unique<AudioEngine>(*extractor, *callback);
audioEngine->setFileName(uri);
audioEngine->start();
}
audioEngine
使用 oboe 库进行音频处理,所以我无法控制其中可能的线程创建,因此 AttachCurrentThread()
在我的回调 class.附加和分离工作,唯一失败的是 Java.
我主要是自己解决了这个问题(阅读@Michael 在评论中的建议以及那里提到的其他一些地方肯定有帮助!):
首先,我将 "setup" 部分与实际的回调函数分开(这一直是计划,但不这样做首先阻碍了看到问题的方式)。
其次,在我的 native_lib.cpp
中,我在传递之前创建了对 jobject
的全局引用(问题示例中 playFromJNI
中的 "instance" 对象)像这样进入 AudioCallback's
构造函数:
myJNIClass = env->NewGlobalRef(instance);
callback = std::make_unique<AudioCallback>(*g_jvm, myJNIClass);
第三,在实际的回调方法中 playbackProgress
我只在必要时处理 ÀttachToCurrentThread`,并使用我事先用正确数据初始化的 class 成员。
这就是我的 AudioCallback
实现现在的样子:
#include <jni.h>>
#include <utils/logging.h>
#include "AudioCallback.h"
jclass target;
jmethodID id;
AudioCallback::AudioCallback(JavaVM &jvm, jobject object) : g_jvm(jvm), g_object(object) {
JNIEnv *g_env;
int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
LOGD("Env Stat: %d", getEnvStat);
if (g_env != NULL) {
target = g_env->GetObjectClass(g_object);
id = g_env->GetMethodID(target, "integerCallback", "(I)V");
}
}
void AudioCallback::playBackProgress(int progressPercentage) {
JNIEnv *g_env;
int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
LOGD("GetEnv: not attached - attaching");
if (g_jvm.AttachCurrentThread(&g_env, NULL) != 0) {
LOGD("GetEnv: Failed to attach");
}
} else if (getEnvStat == JNI_OK) {
LOGD("GetEnv: JNI_OK");
} else if (getEnvStat == JNI_EVERSION) {
LOGD("GetEnv: version not supported");
}
g_env->CallVoidMethod(g_object, id, (jint) progressPercentage);
}
这现在可以按我的需要工作,但由于我不是 C++/JNI 专家,我的代码中可能仍然存在问题。请随时指出它们!