使用JNI在Java调用之间在内存中保留本机对象



我有一个Android应用程序,需要使用C库。我用JNI来连接它。该库使用一个结构体(姑且称之为foo)。foo与一组初始参数一起工作,其中包括指向C函数的指针,它用于从我的应用程序请求更多数据,将该数据纳入计算过程。一旦它拥有了所需的一切,它就会通过C回调函数返回结果,它还需要一个指针。我需要将所有这些C回调函数挂钩到我的应用程序,以从用户获取更多数据,将数据返回到foo,并最终通过最终回调函数将结果显示给我的应用程序中的用户。

我创建了foo_callbacks -刚刚定义了静态C函数,我在初始化时传入foo,在这些函数中,我使用JNI再次调用我的应用程序(还没有测试过这个,但我也保留了对jvm的引用,并从中获得JNIEnv并附加到当前线程,像这样)。

但这是怎么回事:

    调用JNI,用指向foo_callbacks的静态函数的指针初始化foo。我保持对foo的静态引用。
  1. 单独调用JNI使用现有foo对象启动计算过程
  2. 但是当foo需要使用我之前传递的回调函数时,我得到这个消息:A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x4 in tid 14244

在谷歌上,似乎我试图访问的内存不再属于我的应用程序。所以我认为那些回调函数的引用不再有效了。所以我的问题是,我如何在JNI调用之间保持内存中的本机对象?或者有其他方法来解决这个问题吗?谢谢。

下面是一些示例代码:

FooManager.java

...
static {
    System.loadLibrary("FooLib");
}
//initialized Foo library
private native int fooLibInit();
//start the process 
public native int fooStart(String message);
//continue the process after a delay 
public native int fooContinue(String message);
//retrieve milliseconds to schedule next event
public native long fooGetMsToNextEvent();
//method that gets called from native code
public static long getCurrentTime(){
    return System.currentTimeMillis();
}
//method that gets called from native code, returning results
public static void deliverResult(String result){
    //display result to the user
}
...

FooScheduler.java

...
public static void kickItOff(String message){
    FooManager.fooLibInit();
    long timeToWait = FooManager.fooGetMsToNextEvent();
    //this call figures out what step it is and gets some data
    SchedulerUtil.scheduleCallBack(timeToWait);
}
//this is a callback function that gets called after given about of time by SchedulerUtil
public static void callBack(int step, String message){
    if(step == 1)
        FooManager.fooStart(message)
    else FooManager.fooContinue(message);
}
...

FooLib.cpp

#include <string.h>
#include <jni.h>
#include <android/log.h>
extern "C" {
    #include "blackbox.h"
    #include "foo_wrapper.h"
}
extern "C" {
    static jboolean isJni();
    //struct defined in blackbox.h
    static foo foo_obj;
    JNIEXPORT jint
    JNI_OnLoad(JavaVM *vm, void *reserved) {
        //defined in foo_wrapper.h
        set_java_env(vm);
        return JNI_VERSION_1_6;
    }
    JNIEXPORT jint JNICALL
    Java_com_myapp_fooInit(JNIEnv * env, jobject obj){
        //foo_get_global_time_wrapper and foo_return_result_wrapper functions is defined in foo_wrapper.h.
        //those pointers are actually a member variables of foo_obj,
        //they gets assigned in the fooInit() so foo_obj can use them later. fooInit is defined in blackbox.h
        int resultInit = fooInit(&foo_obj, foo_get_global_time_wrapper, foo_return_result_wrapper);
        return resultInit;
    }
    JNIEXPORT jint JNICALL
    Java_com_myapp_fooStart(JNIEnv * env, jobject obj, jstring message){
        jboolean copy = isJni();
        const char *firstCharPointer = env->GetStringUTFChars(message, &copy);
        //here is where the foo_get_global_time_wrapper function is called, and
        //
        //I am getting A/libc: Fatal signal 11 (SIGSEGV) error.
        //
        //fooStart is defined in blackbox.h
        int resultCode = fooStart(&foo_obj, (uint8*)firstCharPointer, strlen(firstCharPointer));
        return resultCode;
    }
    JNIEXPORT jint JNICALL
    Java_com_myapp_fooContinue(JNIEnv * env, jobject obj, jstring message){
        jboolean copy = isJni();
        const char *firstCharPointer = env->GetStringUTFChars(chunk, &copy);
        //here blackbox produces results based on the first and second messages that were passed in and calls foo_return_result_wrapper with results
        //fooContinue is defined in blackbox.h
        int resultCode = fooContinue(&foo_obj, (uint8*)firstCharPointer, strlen(firstCharPointer));
        return resultCode;
    }

    static jboolean isJni(){
        return JNI_TRUE;
    }
}

foo_wrapper.c

#include "foo_wrapper.h"
#include <jni.h>
#include <string.h>
static JavaVM *JVM;
extern uint32 foo_get_global_time_wrapper() {
    JNIEnv *env;
    int result = (*JVM)->GetEnv(JVM, (void **) &env, JNI_VERSION_1_6);
    if (result != JNI_OK) {
        LOGI("couldnt get JVM.");
        return 1;
    }
    jclass clazz = (*env)->FindClass(env, "com/myapp/FooManager");
    jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "getCurrentTime", "()J");
    long milliseconds;
    (*env)->CallStaticObjectMethod(env, clazz, mid, milliseconds);
    return milliseconds;
}
extern int foo_return_result_wrapper(const uint8 *start, uint16 length) {
    JNIEnv *env;
    int result = (*JVM)->GetEnv(JVM, (void **) &env, JNI_VERSION_1_6);
    if (result != JNI_OK) {
        LOGI("couldnt get JVM.");
        return 1;
    }
    jstring result = //get jstring from parameters start and length;
    jclass clazz = (*env)->FindClass(env, "com/myapp/FooManager");
    jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "deliverResult", "(LJava.lang.String;)J");
    jobject obj =   (*env)->CallStaticObjectMethod(clazz, mid, result);
    return 0;
}
extern void set_java_env(JavaVM *vm) {
    JVM = vm;
}

请注意,这不是测试代码-它基本上是一个更简单的版本,我正在尝试做的

这就是服务被设计用来解决的问题。有三种类型:与用户交互的前台服务;后台服务,在后台做事情;和绑定服务(客户端/服务器),只要客户端应用绑定到它们,它们就存在。将JNI代码实现为绑定服务,并在其上使用Java的薄包装器;然后你会得到你的坚持。

这是多线程问题。不能保证JNI将在与Java相同的线程上执行本机代码。将所有native函数设置为synchronized解决了这个问题。

//initialized Foo library
private native synchronized int fooLibInit();
//start the process 
public native synchronized int fooStart(String message);
//continue the process after a delay 
public native synchronized int fooContinue(String message);
//retrieve milliseconds to schedule next event
public native synchronized long fooGetMsToNextEvent();
//method that gets called from native code
public static synchronized long getCurrentTime(){
    return System.currentTimeMillis();
}
//method that gets called from native code, returning results
public static synchronized void deliverResult(String result){
    //display result to the user
}

最新更新