我试图在Android上构建一个回调类,从我的原生代码中的不同线程调用Java方法。我读过很多关于如何做到这一点的书,只要我在同一条线上,这一切都是有效的。但是,从另一个线程中,我无法正确检索JNIEnv
,也无法找出我做错了什么。
我对C和JNI不是很有经验,所以这很可能是初学者的问题。。。但我已经花了好几天时间,看不出它是什么。
这是我的回调类,. h和. cpp文件:
class AudioCallback {
public:
explicit AudioCallback(JavaVM&, jobject);
void playBackProgress(int progressPercentage);
private:
JavaVM& g_jvm;
jobject g_object;
};
jclass target = NULL;
jmethodID id = NULL;
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);
if (g_env != NULL) {
target = g_env->GetObjectClass(g_object);
id = g_env->GetMethodID(target, "integerCallback", "(I)V");
//This is a test call to see if I can call my java method. It works.
g_env->CallVoidMethod(g_object, id, (jint) 103);
}
}
// this method is calles from other threads, so I want to attach to the current thread once I got my JNIEnv, but I can't since it's null...
void AudioCallback::playBackProgress(int progressPercentage) {
JNIEnv *g_env;
// This is null and I don't know why!
int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
if (g_env == NULL) {
LOGE("JNIEnv in callback method is null");
} else {
LOGD("Env Stat: %d", getEnvStat);
JavaVMAttachArgs vmAttachArgs;
if (getEnvStat == JNI_EDETACHED) {
LOGD("GetEnv: not attached - attaching");
if (g_jvm.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");
}
g_env->CallVoidMethod(g_object, id, (jint) progressPercentage);
//thread gets detached elsewhere
}
}
这是我的原生_库,在这里我获得了JavaVM
,并实例化了回调类:
std::unique_ptr<AudioEngine> audioEngine;
std::unique_ptr<AudioCallback> callback;
JavaVM *g_jvm = nullptr;
static jobject myJNIClass;
jint JNI_OnLoad(JavaVM *pJvm, void *reserved) {
g_Jvm = pJvm;
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL
Java_com_my_appy_common_jni_JniBridge_playFromJNI(JNIEnv *env, jobject instance,jstring URI) {
myJNIClass = env->NewGlobalRef(instance);
callback = std::make_unique<AudioCallback>(*gJvm, myJNIClass);
// this test call to my callback works
callback->playBackProgress(104);
const char *uri = env->GetStringUTFChars(URI, NULL);
//... urelated code is left out here ...
//audioEngine gets the callback and uses it from threads it creates
audioEngine = std::make_unique<AudioEngine>(*extractor, *callback);
audioEngine->setFileName(uri);
audioEngine->start();
}
我缩短了代码,删除了所有不相关/不必要的部分。如果缺少关键的东西,请评论,我会补充的。
解决方案:根据迈克尔在他的回答中提出的建议,我对回调类中的playbackAdvanced
方法进行了这些编辑,以使其工作:
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);
// mJvm.DetachCurrentThread();
}
现在它直接检查getEnvStat
的值,之前的g_env
的空检查是错误的。我还必须将JavaVMAttachArgs
替换为NULL
,以使其正常工作。
playBackProgress
中的逻辑错误。
只有当g_env
为非空时,才尝试连接当前线程。但是如果g_env
是非空的,那么GetEnv
可能成功了(当然,您还应该检查getEnvStat==JNI_OK
)并且AttachCurrentThread
不是必需的。
当g_env
为NULL且getEnvStat
为JNI_EDETACHED
时,需要调用attachMONtThread
。
您还需要跟踪您是否调用了attachMONtThread
,因为在这种情况下,您应该在某个时候调用DetachMONtThread
。有关更多信息,请参见此答案。
首先,我决定让我的类阻塞(让消费者更容易使用,但对我来说可能更乏味)。而不是让使用者定义异步回调。这是一个好的设计模式吗?这样,用户可以获得预期的行为,但如果他们对线程被阻塞的时间不满意,则可以实现自己的多线程。 我有一个构造函数,它根据异步回调的结果在类中设置最后一个字段: 这不起作用,所以我使用了原子引用,并实现了一个阻塞循环,直到返回结果,如下所示: 这是阻止/检索结果的好方法吗?
不管什么原因,我的回拨电话打不通。基本上,我将使用我创建的tcp服务器进行群组聊天。我试图在java中创建一个线程,然后使用回调将我从套接字获得的信息提供给主活动。 当前收到得错误: 客户端任务 最后,我想通过响应事件传递一个字符串,它将获得它的主要内容。
问题内容: d3.drag的文档指出拖动事件的DOM元素目标可用于回调: 调度指定事件时,将使用与select相同的上下文和参数调用每个侦听器。在侦听器上:当前数据d和索引i,并且此上下文作为当前DOM元素。 但是我的回叫是一个对象实例,并指向该对象。因此,我需要另一种访问通常传入的当前DOM元素的方法。我该怎么做? 问题答案: 将第二个和第三个参数一起使用以获取何时不可用: 有关详细说明,请看下
问题内容: 我已经阅读了回调的Wikipedia定义,但仍然不明白。谁能解释一下回调是什么,尤其是以下几行 在计算机编程中,回调是对可执行代码或一段可执行代码的引用,该代码作为参数传递给其他代码。这允许较低层的软件层调用较高层中定义的子例程(或函数)。 问题答案: 也许一个例子会有所帮助。 您的应用程序想要从一台远程计算机下载文件,然后写入本地磁盘。远程计算机是拨号调制解调器和卫星链路的另一侧。延
我有一个ViewModel处理我的业务逻辑,我正在使用Koin将它注入到我的活动和每个片段中。然而,在我从片段A-片段B导航并导航回片段A之后,我的观察者再次被触发。为什么会发生这种情况?当我返回时,如何阻止这种onChanged被触发? 我尝试将'this'和'view LifecycleOwner'设置为LiveData的LifecycleOwner。 我还尝试将observable移动到on
每个USB设备都必须根据设备驱动程序将在Linux系统上使用的一些USB设备类定义... 但是我不能理解一些事情。例如,大多数USB调制解调器都属于通信设备类。我有一个3G USB调制解调器和一个3G USB加密狗(例如,塔塔光子),两者都属于相同的通信类别,但3G USB调制解调器使用CDC-ACM驱动程序,3G加密狗使用串行转换器驱动程序(USB-Serial)。是什么让这些设备与众不同? 有