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

使用JNI在Java调用之间将本机对象保留在内存中,java,android,c,android-ndk,java-native-interface,Java,Android,C,Android Ndk,Java Native Interface,我有一个Android应用程序,需要使用C库。 我正在使用JNI与它进行接口。该库使用一个结构(我们称之为foo)foo使用一组初始参数,其中指向C函数的指针用于从我的应用程序请求更多数据,并将这些数据合并到计算过程中。一旦它拥有了所需的一切,它就会通过C回调函数返回一个结果,它还需要一个指针。我需要将所有的C回调函数挂接到我的应用程序,从用户那里获取更多数据,将数据返回到foo中,最后通过最终的回调函数将结果显示给我应用程序中的用户 我创建了foo\u回调——刚刚定义的静态C函数,在初始化时传

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

我创建了
foo\u回调
——刚刚定义的静态
C
函数,在初始化时传递给
foo
,在这些函数中,我使用
JNI
再次调用我的应用程序(还没有测试过这个,但我还保留了对
jvm
的引用,并从中获取
JNIEnv
,并附加到当前线程,)

但事实是:

  • 调用
    JNI
    初始化
    foo
    ,并使用
    foo\u回调中指向静态函数的指针。我保留对
    foo
    的静态引用
  • JNI
    的单独调用使用现有
    foo
    对象启动计算过程
  • 但是当
    foo
    需要使用我之前传递的回调函数时,我会收到以下消息:
    a/libc:tid 14244中的致命信号11(SIGSEGV),代码1,故障地址0x4
  • 谷歌搜索时,我似乎正在尝试访问内存。因此我认为对回调函数的引用不再有效。所以我的问题是,如何在
    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
    }
    
    ...
    
    ...
    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);
    }
    ...
    
    FooScheduler.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
    }
    
    ...
    
    ...
    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);
    }
    ...
    
    bullib.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;
        }
    }
    

    请注意,这不是经过测试的代码-它基本上是我尝试执行的一个简单得多的版本。

    这是一个多线程问题。不能保证
    JNI
    将在
    Java
    的同一线程上执行本机代码。使所有
    本机
    函数
    同步
    解决了这个问题

    //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
    }
    

    这就是服务设计要解决的问题。有三种风格:与用户交互的前台服务;在后台做事情的后台服务;绑定服务(客户端/服务器)只要客户端应用程序绑定到它们,就存在。将JNI代码作为一个绑定的服务,在其上面加上一个java的瘦包装;然后你就可以得到持久性。

    C中没有类的东西。你是指C++类,或者你的意思是完全不同的?这有助于一些,但是我仍然不能理解你的Stand。一些代码可能会有帮助。具体问题:1.不清楚如何"在Java中保持对
    foo
    的静态引用,因为它是一个C结构。2.不清楚
    foo
    本身的内存在本机端是如何管理的。3.根本不清楚
    foo\u回调是什么-这是一个文件还是某个结构?4.不清楚如何开始调用JVM-我遵循了链接,但细节是疾病很重要,所以您如何做可能很重要。@Brick:公平地说,我将在一篇文章中发布示例代码bit@Brick好的,我添加了一些示例代码。如果您查看logcat输出,您将找到堆栈跟踪。您可以使用工具对其进行分析,并找到导致崩溃的确切代码行。在Dalvik下,从Java语言代码在同一个线程上运行。如果您看到一个线程在另一个线程上运行,那是因为您的应用程序中有两个不同的线程,而不是在JNI调用时有一个线程分裂。地址0x4处的SIGSEGV看起来像一个空指针解引用;与内存流失无关。FWIW您可以通过编写lo来轻松识别线程g消息并查看线程ID…
    adb logcat-v threadtime
    如果您不想配置Android Studio。我不知道为什么
    synchronized
    会有任何不同。