使用JNI

时间:2015-10-20 23:00:05

标签: java android c android-ndk java-native-interface

我有一个Android应用,需要使用C库。 我正在使用JNI与它进行交互。该库使用结构(让我们称之为foo)。 foo使用一组初始参数,其中包含指向C函数的指针,用于从我的应用程序请求更多数据,将这些数据合并到计算过程中。一旦它拥有它需要的一切,它就会通过一个C回调函数返回一个结果,它还需要一个指针。我需要将所有C回调函数挂钩到我的应用程序以从用户获取更多数据,将该数据返回到foo,最后通过最终回调函数将结果显示给我的应用程序中的用户。 / p>

我创建了foo_callbacks - 刚刚定义的静态C函数,我在初始化时传递给foo,在这些函数中,我使用JNI再次调用我的应用程序(避难所& #39; t对此进行了测试,但我也保留了对jvm的引用并从中获取JNIEnv并附加到当前主题like this)。

但是发生了什么:

  1. 致电JNI,使用指向foo的静态函数的指针初始化foo_callbacks。我保留了对foo
  2. 的静态引用
  3. JNI的单独调用使用现有的foo对象
  4. 启动计算过程
  5. 但是当foo需要使用我之前传递的回调函数时,我收到此消息:A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x4 in tid 14244
  6. 通过谷歌搜索,似乎是我试图访问isn't owned by my app anymore的内存。所以我认为那些对回调函数的引用不再有效。所以我的问题是,如何在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;
    }
    

    请注意,这不是经过测试的代码 - 它基本上是我想要做的更简单的版本。

1 个答案:

答案 0 :(得分:0)

这是一个多线程问题。无法保证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
}