cie*_*ech 9 c++ java-native-interface android
我刚刚开始使用JNI,我遇到了以下问题.
我有一个C++库,它有一个简单的类.我有三个从Java Android项目调用的JNI方法,它们实例化所述类,分别在实例化类上调用一个方法并销毁它.我保留了对这个对象的全局引用,因此在其他两个JNI方法中我可以使用它.
我怀疑我不能这样做.当我运行应用程序时,我收到运行时错误(使用引用陈旧),我怀疑这是因为在后续调用其他JNI方法时全局引用无效.
是实现我想要的唯一方法(让对象存在多个JNI调用),实际将指向实例化类的指针传回给Java,将其保存在那里,然后将其传递回JNI函数?如果是这样,那很好,我想确保我不能用全局参考来做,而且我不只是遗漏了一些东西.
我已经阅读了有关JNI中全局/本地引用的文档和章节,但它似乎只适用于Java类,而不适用于我自己的本机C++类,或者我错了.
这是代码,如果我的描述不清楚(总结,我想知道这种持久化对象的机制是否会起作用):
Java的:
package com.test.ndktest;
import android.app.Activity;
import android.os.Bundle;
import android.app.AlertDialog;
public class NDKTestActivity extends Activity {
static {
System.loadLibrary("ndkDTP");
}
private native void initializeTestClass();
private native void destroyTestClass();
private native String invokeNativeFunction();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initializeTestClass();
String hello = invokeNativeFunction();
destroyTestClass();
new AlertDialog.Builder(this).setMessage(hello).show();
}
Run Code Online (Sandbox Code Playgroud)
}
JNI标题:
extern "C" {
jstring Java_com_test_ndktest_NDKTestActivity_initializeTestClass(JNIEnv* env, jobject javaThis);
jstring Java_com_test_ndktest_NDKTestActivity_destroyTestClass(JNIEnv* env, jobject javaThis);
jstring Java_com_test_ndktest_NDKTestActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis);
};
Run Code Online (Sandbox Code Playgroud)
JNI机构:
#include <string.h>
#include <jni.h>
#include <ndkDTP.h> //JNI header
#include <TestClass.h> //C++ header
TestClass *m_globalTestClass;
void Java_com_test_ndktest_NDKTestActivity_initializeTestClass(JNIEnv* env, jobject javaThis) {
m_globalTestClass = new TestClass(env);
}
void Java_com_test_ndktest_NDKTestActivity_destroyTestClass(JNIEnv* env, jobject javaThis) {
delete m_globalTestClass;
m_globalTestClass = NULL;
}
jstring Java_com_test_ndktest_NDKTestActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis) {
jstring testJS = m_globalTestClass->getString();
return testJS;
}
Run Code Online (Sandbox Code Playgroud)
C++标题:
class TestClass
{
public:
jstring m_testString;
JNIEnv *m_env;
TestClass(JNIEnv *env);
jstring getString();
};
Run Code Online (Sandbox Code Playgroud)
C++主体:
#include <jni.h>
#include <string.h>
#include <TestClass.h>
TestClass::TestClass(JNIEnv *env){
m_env = env;
m_testString = m_env->NewStringUTF("TestClass: Test string!");
}
jstring TestClass::getString(){
return m_testString;
}
Run Code Online (Sandbox Code Playgroud)
谢谢
您的实现的问题是jstring数据成员。NewStringUTF()创建一个 JavaString对象以从 JNI 方法返回。所以它是一个Java本地引用。但是,您将其存储在 C++ 对象中并尝试在 JNI 调用中使用它。
您应该更好地区分 C++ 对象、Java 和介于两者之间的 JNI 接口。换句话说,C++ 应该使用 C++ 存储字符串的方式(如std::string)。的 JNI 实现InvokeNativeFunction()应该将其转换为 ajstring作为返回值。
PS:有是需要的C ++实现,以保持引用Java对象(或者反过来)的情况。但是如果做得不好,它会使代码更加复杂并且容易出现内存错误。所以你应该只在它真正增加价值的地方使用它。
我在这个主题上找不到关于 SO 的好答案,所以这是我的解决方案,使对象在 C++ 上保持活动状态,以便从多个 JNI 调用中引用它们:
爪哇
在 Java 方面,我正在创建一个带有long指针的类,以保留对 C++ 对象的引用。将 C++ 方法包装在 Java 类中,允许我们在多个活动中使用 C++ 方法。请注意,我正在构造函数上创建 C++ 对象,并在清理时删除该对象。这对于防止内存泄漏非常重要:
public class JavaClass {
// Pointer (using long to account for 64-bit OS)
private long objPtr = 0;
// Create C++ object
public JavaClass() {
createCppObject();
}
// Delete C++ object on cleanup
public void cleanup() {
deleteCppObject();
this.objPtr = 0;
}
// Native methods
public native void createCppObject();
public native void workOnCppObject();
public native void deleteCppObject();
// Load C++ shared library
static {
System.loadLibrary("CppLib");
}
}
Run Code Online (Sandbox Code Playgroud)
C++
在 C++ 方面,我定义了创建、修改和删除对象的函数。值得一提的是,我们必须使用对象new并将delete其存储在 HEAP 内存中,以使其在 Java 类实例的整个生命周期中保持活动状态。我也存储指针CppObject中直JavaClass,使用getFieldId,SetLongField以及GetLongField:
// Get pointer field straight from `JavaClass`
jfieldID getPtrFieldId(JNIEnv * env, jobject obj)
{
static jfieldID ptrFieldId = 0;
if (!ptrFieldId)
{
jclass c = env->GetObjectClass(obj);
ptrFieldId = env->GetFieldID(c, "objPtr", "J");
env->DeleteLocalRef(c);
}
return ptrFieldId;
}
// Methods to create, modify, and delete Cpp object
extern "C" {
void Java_com_test_jnitest_JavaClass_createCppObject(JNIEnv *env, jobject obj) {
env->SetLongField(obj, getPtrFieldId(env, obj), (jlong) new CppObject);
}
void Java_com_test_jnitest_JavaClass_workOnCppObject(JNIEnv *env, jobject obj) {
CppObject* cppObj = (CppObject*) env->GetLongField(obj, getPtrFieldId(env, obj));
// Write your code to work on CppObject here
}
void Java_com_test_jnitest_JavaClass_deleteCppObject(JNIEnv *env, jobject obj) {
CppObject* cppObj = (CppObject*) env->GetLongField(obj, getPtrFieldId(env, obj));
delete cppObj;
}
}
Run Code Online (Sandbox Code Playgroud)
笔记:
delete.GetFieldID,SetLongField和GetLongField来存储来自 C++ 的对象引用,但您也可以jlong像其他答案中讨论的那样存储来自 Java的对象指针。JavaObject类实现为 aParcelable以便将我的类传递给使用Intent附加功能的多个活动。| 归档时间: |
|
| 查看次数: |
6930 次 |
| 最近记录: |