当前位置: 首页 > 知识库问答 >
问题:

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

欧博简
2023-03-14

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

我创建了foo_callbacks-刚定义了静态C函数,在初始化时将其传递到foo中,在这些函数中,我使用JNI再次调用我的应用程序(还没有对此进行测试,但我也保留了对JVM的引用,并从中获取JNIENV并附加到当前线程,如下所示)。

但是正在发生的事情是:

  1. 调用JNI来初始化foo,并使用指向foo_callbacks中静态函数的指针。我保留了对foo的静态引用。
  2. 单独调用JNI将使用现有的foo对象
  3. 启动计算过程
  4. 但当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);
}
...

folib.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个答案

佟寒
2023-03-14

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

 类似资料:
  • 我有一个Java应用程序,它合并了一些大文件。Java应用程序不在我的控制之下。Java应用程序的结果以大约90MB的大字符串返回给我的C程序,我在其中的一些算法中使用它。我多次调用execute方法。我的问题是,每次调用Java应用程序时,它都会保留更多内存,但不会释放内存。从这个垃圾收集和JNI调用问题中,我想到手动调用垃圾收集器,但它不会释放内存。有没有办法解决这个问题? 这是我的C程序

  • 我实现了一个自定义POSIX信号处理程序参考:http://blog.httrack.com/blog/2013/08/23/catching-posix-signals-on-android/ 它们似乎是艺术的平台限制。 通过信号处理程序方法从JNI调用java方法,是否有变通方法或任何其他实现方法。 如果没有,那么是否有替代方案来捕获本机崩溃并传播到应用程序?

  • 我有一个通过JNI调用C++共享对象的Java对象。在C++中,我保存了对JNIEnv和JObject的引用。 我还有一个GLSurface呈现器,它最终在一个不同的线程glthread上调用上面提到的C++共享对象。然后,我试图使用我最初保存的jobject回调到我最初的Java对象,但我想,因为我在GLThread上,我得到了以下错误。 回Java的代码: } 如果我使用env->newobj

  • 我正在制作一个库应用程序,该应用程序使用谷歌断路器检测android中的本机崩溃。每当我的主端发生本机崩溃时,断路器都会调用以下回调。从这个回调中,我需要使用JNI在java类中调用一个静态val方法。 这是我的java方法: 在Android5.0之前,这一功能一直运行良好。但在Lollipop中,我无法调用我的java方法,因为我无法在Logcat控制台上看到“内部句柄异常”日志。 以下是我在

  • 问题内容: 我有一个C程序,它使用JNI在Java存储中存储一些对象。(在有人问之前,这里需要使用Java存储,而我必须用C编写一个客户端,该客户端才能从该存储中添加和检索对象)。 我制作了程序,并尝试添加100000个大小为1KB的对象。但是在仅添加50000个对象之后,我得到了“内存不足”消息(请注意,每当我无法使用NewStringUTF和NewByteArray函数分配新的字符串或字节数组

  • 问题内容: 当我使用JNI方法构建Java对象时,为了将其作为参数传递给我使用JNI调用API进行调用的Java方法,如何管理它的内存? 这是我正在处理的内容: 我有一个C对象,该对象的析构函数要复杂得多。这个C对象将与Java对象相关联,一旦应用程序完成了Java对象,我将不再需要C对象。 我正在这样创建Java对象(为了清楚起见,省略了错误检查): 那么,既然我已经完成了,该怎么办?理想情况下