【问题标题】:Sharing a JavaVM* across threads in Android NDK在 Android NDK 中跨线程共享 JavaVM*
【发布时间】:2012-04-20 05:32:48
【问题描述】:

我想从一个接收来自另一个可执行文件的回调的 cpp 文件调用 Java 类方法。

为了实现这一点,我使用 .cpp 文件中的 android::AndroidRuntime::getJavaVM() 方法检索了一个 JavaVM 指针,该文件直接接收 JNI 方法调用。我通过构造函数将这个 JavaVM 指针共享到最终的 .cpp 文件,我在该文件中调用所需的 Java 方法,如下所示:

/* All the required objects(JNIEnv*,jclass,jmethodID,etc) are appropriately declared. */
**JNIEnv* env;
jvm->AttachCurrentThread(&env, NULL);
clazz = env->FindClass("com/skype/ref/NativeCodeCaller");
readFromAudioRecord = env->GetStaticMethodID(clazz, "readFromAudioRecord", "([B)I");
writeToAudioTrack = env->GetStaticMethodID(clazz, "writeToAudioTrack", "([B)I");** 

但是,我在运行此代码时遇到 SIGSEGV 错误。

根据 JNI 文档,这似乎是在任意上下文中获取 JNIEnv 的适当方式:http://java.sun.com/docs/books/jni/html/other.html#26206

我们将不胜感激。

问候, 尼拉杰

【问题讨论】:

  • 什么是 android::AndroidRuntime::getJavaVM) ?那不是 NDK 公共 API 功能。您正在使用未记录的东西。要在 NDK 中获取 JavaVM*,您必须实现 JNI_OnLoad 全局函数,该函数会在您的共享库被加载时自动调用。
  • 感谢您的回复.. android.wooyd.org/JNIExample/#NWD1sCYeT-J - 本文档很好地介绍了 JNI_OnLoad,其中使用了 android::AndroidRuntime::registerNativeMethods()。你确定 android::AndroidRuntime 没有记录吗?
  • 是的,它在普通用户 NDK 代码中没有记录功能。阅读 JNI 文档(来自 Sun)并检查 NDK 文件夹中的 docs/STABLE-APIS.html 文件以获取其他合法和文档化的 API。
  • 该文档是关于 NDK 出现之前的 Android 编程的。阅读第一部分“重要通知”。
  • 再次感谢您指出这一点。我删除了对 android::AndroidRuntime::getJavaVM() 的调用,并改用 JNI_onLoad 来传播 JavaVM* 指针。但是,仍然得到相同的 SIGSEGV 故障。有什么想法吗?

标签: multithreading android-ndk java-native-interface


【解决方案1】:

如果您尝试使用 JNIEnv 或 JavaVM 引用而不将线程附加到 VM,全局引用将不会阻止新线程中的分段错误。你第一次做得很好,Mārtiņš Možeiko 错误地暗示你所做的事情有问题。

不要删除它,只是学习如何使用它。那家伙不知道他在说什么,如果它在 jni.h 中,你可以很确定它不会去任何地方。它没有被记录的原因可能是因为它是可笑的自我解释。您不需要创建 GlobalReference 对象或任何东西,只需执行以下操作:

#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <android/log.h>
#include <linux/threads.h>
#include <pthread.h>

#define  LOG_TAG    "[NDK]"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

static pthread_mutex_t thread_mutex;
static pthread_t thread;
static JNIEnv* jniENV;

void *threadLoop()
{
    int exiting;
    JavaVM* jvm;
    int gotVM = (*jniENV)->GetJavaVM(jniENV,&jvm);
    LOGI("Got JVM: %s", (gotVM ? "false" : "true") );
    jclass javaClass;
    jmethodID javaMethodId;
    int attached = (*jvm)->AttachCurrentThread(jvm, &jniENV,NULL);
    if(attached>0)
    {
        LOGE("Failed to attach thread to JavaVM");
        exiting = 1;
    }
    else{
        javaClass= (*jniENV)->FindClass(jniENV, "com/justinbuser/nativecore/NativeThread");
        javaMethodId= (*jniENV)->GetStaticMethodID(jniENV, javaClass, "javaMethodName", "()V");
    }
    while(!exiting)
    {
        pthread_mutex_lock(&thread_mutex);
        (*jniENV)->CallStaticVoidMethod(jniENV, javaClass, javaMethodId);
        pthread_mutex_unlock(&thread_mutex);
    }
    LOGE("Thread Loop Exiting");
    void* retval;
    pthread_exit(retval);
    return retval;
}

void start_thread(){
    if(thread < 1)
        {
            if(pthread_mutex_init(&thread_mutex, NULL) != 0)
            {
                LOGE( "Error initing mutex" );
            }
            if(pthread_create(&thread, NULL, threadLoop, NULL) == 0)
            {
                LOGI( "Started thread#: %d", thread);
                if(pthread_detach(thread)!=0)
                {
                    LOGE( "Error detaching thread" );
                }
            }
            else
            {
                LOGE( "Error starting thread" );
            }
        }
}

JNIEXPORT void JNICALL
Java_com_justinbuser_nativecore_NativeMethods_startThread(JNIEnv * env, jobject this){
    jniENV = env;
    start_thread();
}

【讨论】:

  • 我在哪里说过使用 jni.h 中的某些内容是错误的或不允许的?
  • “这不是 NDK 公共 API 函数。您使用的是未记录的东西。要在 NDK 中获取 JavaVM*,您必须实现 JNI_OnLoad 全局函数,该函数会在加载共享库时自动调用。”很抱歉这么说,但整个陈述完全不正确。
  • 这是真的 - android::AndroidRuntime::getJavaVM 不是 jni.h 中的公共函数或方法。我从未说过不允许使用 jni.h 中的函数或方法(例如全局 JNI_OnLoad、JavaVM 或 JNIEnv)。但是你暗示我说他没有正确使用 jni.h 中的函数。他怎么能用 android 内部的 android::AndroidRuntime::getJavaVM 函数正确地做到这一点,而不是在 jni.h 中?
  • 是的,它是 jni.h 中的一个公共函数,除此之外,我还提供了一个示例,说明它在上面的源代码中是如何使用的,如果您费心查看它的话。他所做的是正确的,而你告诉他他错了,这就是我的反应。
  • 这里是来自 NDK r8d 平台\android-14\arch-arm\usr\include\jni.h 的 jni.h 文件:pastebin.com/04RgHUYN 请告诉我androidAndroidRuntime 在哪里命名空间。
【解决方案2】:

解决了这个问题。分段错误是因为我无法从从共享 jvm 指针检索到的 JNIEnv 对象中检索到 jclass 对象。

我随 jvm 一起传播了一个全局引用 jclass 对象,问题就解决了。

感谢您的帮助 Mārtiņš Možeiko!..

问候, 尼拉杰

【讨论】:

    猜你喜欢
    • 2018-02-20
    • 1970-01-01
    • 2016-07-13
    • 2015-03-27
    • 2021-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多