[英]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
並附加到當前線程, 像這樣 )。
但是發生了什么:
JNI
初始化foo
,指向來自foo_callbacks
靜態foo_callbacks
。 我保留了對foo
的靜態引用。 JNI
單獨調用使用現有的foo
對象開始計算過程 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, ©);
//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, ©);
//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.