java - 使用 JNI 从单独的线程调用静态 Java 方法

标签 java android c android-ndk java-native-interface

我正在尝试在 android 中使用 JNI 来制作一个函数指针,我正在使用的 native 库使用它转发它对 java 的调用。

调用initializeStateController 时,会使用pthread_create 创建一个新线程,只要State Controller 的状态发生变化,它就会调用函数指针。

但是,当我尝试在 C 中从 state_exec 调用 GetStaticMethodID 时,出现以下错误:

JNI DETECTED ERROR IN APPLICATION: jclass is an invalid local reference: 0xec500019 (0xdead4321) in call to GetStaticMethodID

这是我当前的代码:

C 代码

state_controller *controller;
JavaVM* gJvm = NULL;
static jclass sc_class;

JNIEnv* getEnv() {
    JNIEnv *env;
    int status = (*gJvm)->GetEnv(gJvm, (void**)&env, JNI_VERSION_1_6);
    if(status < 0) {
         status = (*gJvm)->AttachCurrentThread(gJvm, &env, NULL);
        if(status < 0) {
            return NULL;
        }
    }
    return env;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) {
    gJvm = pjvm;
    JNIEnv *env = getEnv();

    sc_class = (*env)->FindClass(env, "com/my/package/StateController");

    return JNI_VERSION_1_6;
}

int state_exec(int state, int from) {
    JNIEnv *env = getEnv();
    jmethodID mid = (*env)->GetStaticMethodID(env, sc_class, "stateExec","(II)I");
    jint result = (*env)->CallStaticIntMethod(env, sc_class, mid, state, from);

    return (int) result;
}

// This part is unimportant.
// This is just where I hand the function pointer
// to the library to use.

JNIEXPORT void JNICALL 
Java_com_my_package_StateController_initializeStateController
(
        JNIEnv *env,
        jobject jobj
) {
    controller = new_state_controller(
        state_exec
    );
    sc_start_controller(controller);
}

Java

package com.my.package;

class StateController {

    public native void initializeStateController();

    public static int stateExec(int state, int from) {
        Log.d("StateTest", "State " + state + " -> " + from);
        return 0;
    }
}

. . . 

(new StateController()).initializeStateController();

最佳答案

事实证明,FindClass 方法只返回本地引用,而不是全局引用。为了完成这项工作,您需要将其设为全局引用。您可以按照下面的说明执行此操作。

// Replace

    sc_class = (*env)->FindClass(env, "com/my/package/StateController");

// With

    sc_class = (*env)->NewGlobalRef(
        env, 
        (*env)->FindClass(
            env, 
            "com/my/package/StateController"
        )
    );

// Later when you are done with the class reference, run this to free it.

    (*env)->DeleteGlobalRef(env, sc_class);

来自 Android 文档:

Bug: Mistakenly assuming FindClass() returns global references

FindClass() returns local references. Many people assume otherwise. In a system without class unloading (like Android), you can treat jfieldID and jmethodID as if they were global. (They’re not actually references, but in a system with class unloading there are similar lifetime issues.) But jclass is a reference, and FindClass() returns local references. A common bug pattern is “static jclass”. Unless you’re manually turning your local references into global references, your code is broken.

https://android-developers.googleblog.com/2011/11/jni-local-reference-changes-in-ics.html

关于java - 使用 JNI 从单独的线程调用静态 Java 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46332147/

相关文章:

c - 如何以编程方式在 OpenSSL 中创建自签名证书(即,不使用 'openssl' CLI 命令)?

c - 如何在 alpine 上编译 32 位的 hello world?

java - 从多行字符串中提取文本

android - geolocation.watchposition 不适用于基于网络的位置关闭

android - android中如何在没有ViewSwitcher的情况下将一组TextView转换为EditText?

java - 如何使用 Guava 的 MultiMap 输出唯一键? ( java )

android - ndk 中的 LocalBroadcastManager

javascript - 在 Javascript 中加密并在 Java 中解密 Zip 文件

java - 为什么 Eclipse 将我的整个项目导出为可运行的 JAR 文件?

java - WebSphere/Rational Application Developer(RAD),构建的 tmp 目录?