我有一个问题。我想编写一个Android应用程序,它使用我的旧的C++类。我必须在整个应用程序生命周期中保持C++对象的活动状态。
我以前用C#编写了一个类似的应用程序,并通过将指向C++类的指针传递到C#并使用IntPtr存储它来解决了这个问题。然后,当我想要调用该对象上的方法时,我只需再次将该指针传递给C++,转换为类指针并在其上调用方法。
我如何在Java和Android NDK中实现类似的结果? Java是否支持存储指针?
我有一个问题。我想编写一个Android应用程序,它使用我的旧的C++类。我必须在整个应用程序生命周期中保持C++对象的活动状态。
我以前用C#编写了一个类似的应用程序,并通过将指向C++类的指针传递到C#并使用IntPtr存储它来解决了这个问题。然后,当我想要调用该对象上的方法时,我只需再次将该指针传递给C++,转换为类指针并在其上调用方法。
我如何在Java和Android NDK中实现类似的结果? Java是否支持存储指针?
是的,你可以做与C#相同的事情。
要创建你的新C++对象:
jlong
Java_package_name_new(JNIEnv *, jobject) {
return (long)(new CPP_Object());
}
ptr
变量中,并将其传递给所有需要它的NDK方法:void
Java_package_name_doSomething(JNIEnv *, jobject, jlong ptr) {
CPP_Object *obj = (CPP_Object *)ptr;
// do whatever you want with the object
}
最后,可以使用类似以下的内容将其删除:
void
Java_package_name_delete(JNIEnv *, jobject, jlong ptr) {
delete (CPP_Object *)(ptr);
}
不必将ptr
传递给所有需要它的方法,您还可以使用SetLongField
和GetLongField
方法直接从NDK部分获取并设置它:这样允许Java ptr
变量仅从代码的NDK部分管理,我认为这更安全且更易于管理。
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");
}
}
C++
在C++方面,我正在定义函数来创建、修改和删除对象。需要注意的是,我们必须使用new
和delete
将对象存储在堆内存中,以使其在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;
}
}
注意事项:
delete
。GetFieldID
、SetLongField
和GetLongField
来存储C++中的对象引用,但你也可以像其他答案中讨论的那样,存储Java的jlong
对象指针。JavaObject
类实现为一个Parcelable
,以便使用extras和Intent
在多个活动之间传递我的类。
long
/jlong
而不是int
/jint
表示指针;否则,如果指针变成 64 位,则代码将会崩溃。 - faddenGetLongField
的缺点是必须将字段ID存储为静态变量,如果需要多个C对象的实例,则可能会出现问题。或者,您可以在每次调用JNI时调用GetFieldID
,但这样会产生查找成本。或者,您可以将字段ID传回Java,这有点混乱,因为现在您必须在Java类中保留字段和字段ID的成员。虽然我不是Java专家,但欢迎任何人告诉我我错了,这样我就可以学到东西。 - orodbhen