JNI теряет ссылку на нативные методы - PullRequest
2 голосов
/ 16 марта 2010

В качестве примера для последующего использования в 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 ]

2 голосов
/ 17 марта 2010

Прочтите главу Спецификация JNI по GlobalRefs. Вы не можете хранить значения jobject или jclass в статических переменных, только GlobalRefs.

1 голос
/ 18 марта 2010

После тщательного обдумывания этого невозможно вызвать нативный метод, реализованный в том же общем объекте в 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);
}
...