我可以创建一个 c++ class 实例并将其用作 Android JNI 中的实体吗?
Can I create a c++ class instance and use it as an entity in Android JNI?
假设我在 cpp 中创建了一个 class 'Car'。
我想用 cpp 中的空构造函数创建 class 的实例。
我可以这样做并在 android 上的 java 代码中使用它吗?
例如:
Java代码
Car myCar = new Car();
CPP class
class Car{
std::string model;
int creationYear;
Car(){}
}
感谢您的帮助
是的。你可以很容易地拥有一个本机 object 来隐藏 Java object - 假设你可以调用 C++ Car()
构造函数。您可以使用 C++ Car
class 中的 public static
方法来做到这一点。
这有点 hack,但是 Java long
保证是 64 位,所以它足够长以保存本机指针值。
在Java中:
public class Car
{
// A Java long is 64 bits, so it will
// hold a native pointer
private long nativeCar;
private native long newNativeCar();
private native void deleteNativeCar( long car );
public Car()
{
this.nativeCar = newNativeCar();
}
// allow for explicit native cleanup by caller
public synchronized deleteCar()
{
if ( 0 != this.nativeCar )
{
deleteNativeCar( nativeCar );
nativeCar = 0;
}
}
// handle cases where the native cleanup code
// was not called
@Override
protected void finalize() throws Throwable
{
deleteCar();
super.finalize();
}
}
编译它,然后在 class
文件上使用 javah
来创建您的 C header 文件。 (请注意,JNI 使用 C,而不是 C++。您可以编写 C++ 代码来实现您的逻辑,但呈现给 JVM 的接口必须是C接口.)
您将在您的原生 header 中获得一些功能,类似于这样(我已经从 javah
中删除注释 - 您需要保留它...) :
jlong some_class_path_newNativeCar( JNIEnv *, jobject );
void some_class_path_deleteNativeCar( JNIEnv *, jobject, jlong );
然后您可以实现您的 C++ 代码:
jlong some_class_path_newNativeCar(
JNIEnv *env, jobject obj )
{
Car *nativeCar = new Car();
// C cast - we're returning a C value
return( ( jlong ) nativeCar );
}
void some_class_path_deleteNativeCar(
JNIEnv *env, jobject obj, jlong jNativeCar )
{
Car *cppNativeCar = ( Car * ) jNativeCar;
delete cppNativeCar;
}
我特意让代码保持简单 - 我遗漏了很多东西。例如,javah
可能有点难以弄清楚如何正确使用。但是,如果您不知道如何正确使用 javah
,那么无论如何您都不应该编写 JNI 代码。
因为 JNI 代码脆弱。您不能犯任何错误,否则最终会出现看似随机的故障或让您的 JVM 崩溃,而崩溃可能不容易追溯到导致问题的错误代码。使用提供给本机调用的 JNIEnv *
指针进行 JNI 调用有很多规则。例如,通常你不能保存 JVM 传递给你的值并在你接收它们的上下文之外使用它们 - 在另一个线程中使用它们,或者在它们传递给你的函数之后使用它们 returns 是导致我上面提到的那些 JVM 崩溃的好方法。
如果之前的调用有任何未决的异常,您也不能对 Java 进行 任何 JNI 调用 - 同样,如果您这样做,您将面临不可预测的错误和崩溃的风险。
因此请保持本机代码简单。
如果可以,将所有逻辑和处理仅保留在 Java/native 界面的一侧。
如果可以,请仅通过 Java/native 接口传递 C 或 Java 字符串、原语或原语数组。
从本机代码与实际的复杂 Java object 交互将需要很多很多行 C 或 C++ 代码来安全地复制 Java 中可以用一两个代码完成的事情代码行。如果复制所有需要的异常和故障检查,即使是简单的 one-line get*()
/set*()
Java 调用也会变成 20 或 30 行或更多的 C 或 C++ 代码无论传入什么数据,都保证在本机调用后安全执行 JVM。如果将 null
传递给无法处理 null
值的 Java 方法,它将抛出 NullPointerException
您可以 catch
并且您的 JVM 运行愉快或干净地关闭并出现未捕获的异常。将 NULL
传递给无法处理它的 JNI 函数,如果您没有正确检查任何故障或任何异常然后正确处理它,您的 JVM 将表现出看似无关的故障或只是崩溃。
假设我在 cpp 中创建了一个 class 'Car'。 我想用 cpp 中的空构造函数创建 class 的实例。 我可以这样做并在 android 上的 java 代码中使用它吗?
例如:
Java代码
Car myCar = new Car();
CPP class
class Car{
std::string model;
int creationYear;
Car(){}
}
感谢您的帮助
是的。你可以很容易地拥有一个本机 object 来隐藏 Java object - 假设你可以调用 C++ Car()
构造函数。您可以使用 C++ Car
class 中的 public static
方法来做到这一点。
这有点 hack,但是 Java long
保证是 64 位,所以它足够长以保存本机指针值。
在Java中:
public class Car
{
// A Java long is 64 bits, so it will
// hold a native pointer
private long nativeCar;
private native long newNativeCar();
private native void deleteNativeCar( long car );
public Car()
{
this.nativeCar = newNativeCar();
}
// allow for explicit native cleanup by caller
public synchronized deleteCar()
{
if ( 0 != this.nativeCar )
{
deleteNativeCar( nativeCar );
nativeCar = 0;
}
}
// handle cases where the native cleanup code
// was not called
@Override
protected void finalize() throws Throwable
{
deleteCar();
super.finalize();
}
}
编译它,然后在 class
文件上使用 javah
来创建您的 C header 文件。 (请注意,JNI 使用 C,而不是 C++。您可以编写 C++ 代码来实现您的逻辑,但呈现给 JVM 的接口必须是C接口.)
您将在您的原生 header 中获得一些功能,类似于这样(我已经从 javah
中删除注释 - 您需要保留它...) :
jlong some_class_path_newNativeCar( JNIEnv *, jobject );
void some_class_path_deleteNativeCar( JNIEnv *, jobject, jlong );
然后您可以实现您的 C++ 代码:
jlong some_class_path_newNativeCar(
JNIEnv *env, jobject obj )
{
Car *nativeCar = new Car();
// C cast - we're returning a C value
return( ( jlong ) nativeCar );
}
void some_class_path_deleteNativeCar(
JNIEnv *env, jobject obj, jlong jNativeCar )
{
Car *cppNativeCar = ( Car * ) jNativeCar;
delete cppNativeCar;
}
我特意让代码保持简单 - 我遗漏了很多东西。例如,javah
可能有点难以弄清楚如何正确使用。但是,如果您不知道如何正确使用 javah
,那么无论如何您都不应该编写 JNI 代码。
因为 JNI 代码脆弱。您不能犯任何错误,否则最终会出现看似随机的故障或让您的 JVM 崩溃,而崩溃可能不容易追溯到导致问题的错误代码。使用提供给本机调用的 JNIEnv *
指针进行 JNI 调用有很多规则。例如,通常你不能保存 JVM 传递给你的值并在你接收它们的上下文之外使用它们 - 在另一个线程中使用它们,或者在它们传递给你的函数之后使用它们 returns 是导致我上面提到的那些 JVM 崩溃的好方法。
如果之前的调用有任何未决的异常,您也不能对 Java 进行 任何 JNI 调用 - 同样,如果您这样做,您将面临不可预测的错误和崩溃的风险。
因此请保持本机代码简单。
如果可以,将所有逻辑和处理仅保留在 Java/native 界面的一侧。
如果可以,请仅通过 Java/native 接口传递 C 或 Java 字符串、原语或原语数组。
从本机代码与实际的复杂 Java object 交互将需要很多很多行 C 或 C++ 代码来安全地复制 Java 中可以用一两个代码完成的事情代码行。如果复制所有需要的异常和故障检查,即使是简单的 one-line get*()
/set*()
Java 调用也会变成 20 或 30 行或更多的 C 或 C++ 代码无论传入什么数据,都保证在本机调用后安全执行 JVM。如果将 null
传递给无法处理 null
值的 Java 方法,它将抛出 NullPointerException
您可以 catch
并且您的 JVM 运行愉快或干净地关闭并出现未捕获的异常。将 NULL
传递给无法处理它的 JNI 函数,如果您没有正确检查任何故障或任何异常然后正确处理它,您的 JVM 将表现出看似无关的故障或只是崩溃。