2010-03-16 59 views
2

作为在Android中以后使用的示例,我编写了一个简单的回调接口。虽然这样做我遇到了以下错误或错误或任何。在C中,两条注释行应该被执行,导致调用C回调onChange。但是,我得到一个UnsatisfiedLinkError。直接在Java中调用本地方法工作得很好。如示例中所示,直接从C中调用它也会产生UnsatisfiedLinkError。我接受任何有关此问题或解决方法的建议,等等。 Java的部分:JNI失去对本地方法的引用

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 

回答

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); 
} 
2

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

+0

为JNI_OnLoad呼吁裁判的唯一功能现在它不应该是一个问题。不过谢谢你指出这个错误,以后可能会引起更多混淆。我相应地更改了代码。 虽然主要问题仍然存在。 – lhw 2010-03-18 07:59:36

+0

@lhw你确定你提供给GetMethodID()的签名是正确的吗?您应该通过javap -s来获取它们,而不是尝试手动构建它们。您还应该分别调用GetMethodID()并将结果测试为零,而不是假设它已经工作。您必须在JNI中非常*防守地进行编程。 – EJP 2011-05-30 05:36:01