簡體   English   中英

使用JNI在Java調用之間保留本機對象

[英]Keeping a native object in memory between calls from Java using JNI

我有一個Android應用程序,需要使用C庫。 我正在使用JNI與它進行交互。 該庫使用結構(讓我們稱之為foo )。 foo使用一組初始參數,其中包括指向C函數的指針,它用於從我的應用程序請求更多數據,將這些數據合並到計算過程中。 一旦它擁有它需要的一切,它就會通過一個C回調函數返回一個結果,它還需要一個指針。 我需要將所有這些C回調函數掛鈎到我的應用程序以從用戶獲取更多數據,將該數據返回到foo ,最后通過最終回調函數將結果顯示給我的應用程序中的用戶。

我創建了foo_callbacks - 剛剛定義的靜態C函數,我在初始化時傳遞給foo ,在那些函數中我使用JNI再次調用我的應用程序(還沒有測試過這個,但我也保留了對jvm的引用並從中獲取JNIEnv並附加到當前線程, 像這樣 )。

但是發生了什么:

  1. 調用JNI初始化foo ,指向來自foo_callbacks靜態foo_callbacks 我保留了對foo的靜態引用。
  2. JNI單獨調用使用現有的foo對象開始計算過程
  3. 但是當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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM