tags:

views:

337

answers:

2

As an example for later use in Android I wrote a simple callback interface. While doing so i ran into the following error or bug or whatever. In C the two commented lines are supposed to be executed resulting in calling the C callback onChange. But instead i get an UnsatisfiedLinkError. Calling the native Method directly in Java works just fine. Calling it directly from C as presented here in the example also produces the UnsatisfiedLinkError. I'm open for any advice concerning this issue or work arounds and so on. The Java Part:

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

The C Part:

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

The header file:

#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

Compilation:

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  A: 

Read the JNI Specification chapter on GlobalRefs. You can't store jobject or jclass values in static variables, only GlobalRefs.

EJP
As the only function being JNI_OnLoad calling the refs right now it shouldn't be a problem. But thanks for pointing out the mistake, it might have caused more confusion later on. I changed the code accordingly.Though the main problem still persists.
lhw
A: 

After carefully thinking this through it's impossible to call an native method implemented in the same shared object in JNI_onLoad without first registering the native methods. As those are loaded after the JNI_onLoad. Here is the solution for the problem:

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