JNI失去了对原生方法的引用

时间:2010-03-16 10:52:09

标签: java c callback java-native-interface

作为以后在Android中使用的一个例子我写了一个简单的回调接口。虽然这样做我遇到了以下错误或错误或其他什么。在C中,应该执行两个注释行,从而调用C回调onChange。但相反,我得到一个UnsatisfiedLinkError。直接在Java中调用本机方法就可以了。如示例中所示,直接从C调用它也会产生UnsatisfiedLinkError。我愿意接受有关此问题或解决方法的任何建议,等等。 Java部分:

import java.util.LinkedList;
import java.util.Random;

interface Listener {
    public void onChange(float f);
}
class Provider {
    LinkedList<Listener> all;
    public Provider() {
        all = new LinkedList<Listener>();
    }
    public void registerChange(Listener lst) {
        all.add(lst);
    }
    public void sendMsg() {
        Random rnd = new Random();
        for(Listener l : all) {
            try {
                l.onChange(rnd.nextFloat());
            }
            catch(Exception e) {
                System.out.println(e);
            }
        }
    }
}
class Inheritance implements Listener {
    static public void main(String[] args) {
        System.load(System.getProperty("user.dir") + "/libinheritance.so");
    }
    public native void onChange(float f);
}

C部分:

#include "inheritance.h"

jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
    JNIEnv *env;
    (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4); 

    jclass inheritance = (*env)->FindClass(env, "Inheritance");
    jobject o_inheritance = (*env)->NewObject(env, inheritance, (*env)->GetMethodID(env, inheritance, "<init>", "()V"));
    jclass provider = (*env)->FindClass(env, "Provider");
    jobject o_provider = (*env)->NewObject(env, provider, (*env)->GetMethodID(env, provider, "<init>", "()V"));

    g_inheritance = (*env)->NewGlobalRef(env, inheritance);
    g_provider = (*env)->NewGlobalRef(env, provider);

    (*env)->CallVoidMethod(env, o_inheritance, (*env)->GetMethodID(env, inheritance, "onChange", "(F)V"), 1.0);

    //(*env)->CallVoidMethod(env, o_provider, (*env)->GetMethodID(env, provider, "registerChange", "(LListener;)V"), o_inheritance);
    //(*env)->CallVoidMethod(env, o_provider, (*env)->GetMethodID(env, provider, "sendMsg", "()V"));

    (*env)->DeleteLocalRef(env, o_inheritance);
    (*env)->DeleteLocalRef(env, o_provider);

    return JNI_VERSION_1_4;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved) {
    JNIEnv *env;
    (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4); 
    (*env)->DeleteGlobalRef(env, g_inheritance);
    (*env)->DeleteGlobalRef(env, g_provider);
 }
JNIEXPORT void JNICALL Java_Inheritance_onChange(JNIEnv *env, jobject self, jfloat f) {
    printf("[C] %f\n", f);
}

标题文件:

#include <jni.h>
/* Header for class Inheritance */

#ifndef _Included_Inheritance
#define _Included_Inheritance
#ifdef __cplusplus
extern "C" {
#endif

jclass g_inheritance, g_provider;

/*
 * Class:     Inheritance
 * Method:    onChange
 * Signature: (F)V
 */
JNIEXPORT void JNICALL Java_Inheritance_onChange(JNIEnv *, jobject, jfloat);

jint JNI_OnLoad(JavaVM *, void *);

#ifdef __cplusplus
}
#endif
#endif

汇编:

gcc -c -fPIC -I /usr/lib/jvm/java-6-openjdk/include -I /usr/lib/jvm/java-6-openjdk/include/linux/inheritance.c inheritance.h
gcc -g -o -shared libinheritance.so -shared -Wl,-soname,libinheritance.so -lc inheritance.o

2 个答案:

答案 0 :(得分:2)

阅读GlobalRefs上的JNI规范章节。您不能将jobject或jclass值存储在静态变量中,只能存储在GlobalRefs中。

答案 1 :(得分:1)

仔细考虑之后,如果没有首先注册本机方法,就无法在JNI_onLoad中调用在同一共享对象中实现的本机方法。因为这些是后加载的JNI_onLoad。以下是该问题的解决方案:

inheritance.c

#include "inheritance.h"

jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
    JNIEnv *env;
    (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4); 

    jclass inheritance = (*env)->FindClass(env, "Inheritance");
    (*env)->RegisterNatives(env, inheritance, methods, 1);

    jobject o_inheritance = (*env)->NewObject(env, inheritance, (*env)->GetMethodID(env, inheritance, "<init>", "()V"));
    jclass provider = (*env)->FindClass(env, "Provider");
    jobject o_provider = (*env)->NewObject(env, provider, (*env)->GetMethodID(env, provider, "<init>", "()V"));

    (*env)->CallVoidMethod(env, o_provider, (*env)->GetMethodID(env, provider, "registerChange", "(LListener;)V"), o_inheritance);
    (*env)->CallVoidMethod(env, o_provider, (*env)->GetMethodID(env, provider, "sendMsg", "()V"));

    return JNI_VERSION_1_4;
}
void onChange(JNIEnv *env, jobject self, jfloat f) {
    printf("[C] %f\n", f);
}

inheritance.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Inheritance */

#ifndef _Included_Inheritance
#define _Included_Inheritance
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     Inheritance
 * Method:    onChange
 * Signature: (F)V
 */
void onChange(JNIEnv *, jobject, jfloat);

JNINativeMethod methods[] = {
    {"onChange", "(F)V", (void *)onChange}
};

jint JNI_OnLoad(JavaVM *, void *);

#ifdef __cplusplus
}
#endif
#endif

inheritance.java

import java.util.LinkedList;
import java.util.Random;

interface Listener {
    public void onChange(float f);
}
class Provider {
    LinkedList<Listener> all;
    public Provider() {
        all = new LinkedList<Listener>();
    }
    public void registerChange(Listener lst) {
        all.add(lst);
    }
    public void sendMsg() {
        Random rnd = new Random();
        for(Listener l : all) {
            try {
                l.onChange(rnd.nextFloat());
            }
            catch(Exception e) {
                System.out.println(e);
            }
        }
    }
}
class Inheritance implements Listener {
    static public void main(String[] args) {
        System.load(System.getProperty("user.dir") + "/libinheritance.so");
    }
    public native void onChange(float f);
}