我可以创建一个 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 将表现出看似无关的故障或只是崩溃。