2010-03-16 61 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