一个本地方法可以从另一个本地方法调用吗?

Can one native method be called from another native method?

我在 Java 中有一个 class,其中包含某些本机方法声明。它在 finalize 方法中包含对 detroy() 的调用,该方法现已弃用。作为最终确定的替代方法,我使用 try-with-resources 实现了 AutoCloseable。但是问题是,必须在我的 JSQ class 中覆盖的 AutoCloseable 提供的 close() 方法与我的代码中已经定义的现有本机 close 方法冲突。如果我能找到一个可以调用 destroy 的备用位置,那应该足以作为解决方案。因此,我试图从本机 close() 调用 destroy(),这将是使用 shdl 的最后一点。这可能/允许/推荐吗?我没有删除或更改本机 close() 的选项,这就是为什么我试图从本机关闭调用销毁。

JSQL.java

class JSQ implements AutoCloseable{
    protected long shdl;
    protected JSQ() { log("JSQ constructor"); }
    protected JSQ(String stmt, boolean isd)
        throws DhSQLException
    {
        // calls a set of native methods
        set_shdl();
        setstmt(stmt, shdl);
        setd(isd, shdl);
        prep(shdl);
    }

    // to be removed
    public void finalize()
    {
        destroy(shdl);
    }

    // alternative to finalize
    @Override
    public void close()
    {
        destroy();
    }

    protected void open()
    {
        parOpen (shdl);
    }

    private native void set_shdl() throws DhSQLException;
    private native void setstmt(String s, long shdl) throws DhSQLException;
    private native void setd(boolean b, long shdl);
    private native void prep(long shdl) throws DhSQLException;
    private native void destroy(long shdl);

    protected native void parOpen(long shdl);
    // following method is called from sub-classes of JSQ
    // super.close(shdl);
    protected native void close(long shdl);


    protected void execute() throws DhSQLException
    {
        parExec(shdl);
    }

    protected native void parExec(long shdl);
}

JSQ.cxx

#define SQ ((sq_stsm_t *)(shdl))

JNIEXPORT void JNICALL Java_com_project_package_JSQ_set_shdl
(JNIEnv *env, jobject obj_This)
{
    jclass cls;
    jmethodID mid;
    cls = (env)->GetObjectClass (obj_This);
    mid = (env)->GetMethodID (hThis,"set_JSQ_shdl","(J)V");

    status_t status;
    // memory allocation
    sq_stsm_t * S = new sq_stsm_t(status);
    if(status)
    {
        if (S) { delete S; }
        return;
    }
    // I understand that we're attempting to call a Java method from a native method.
    // But which method is it calling?
    // Also, are we converting S from sq_stms_t type to jlong?
    (env)->CallVoidMethod (obj_This,mid,(jlong) S);
    return;
}

JNIEXPORT void JNICALL Java_com_project_package_JSQ_setstmt
(JNIEnv *env, jobject, jstring jstmt, jlong shdl)
{
    status_t status;

    // cstmt is obtained using jstmt
    status = SQ->SetStmt(cstmt);
    return;
}

JNIEXPORT void JNICALL Java_com_project_package_JSQ_destroy
(JNIEnv *, jobject, jlong shdl)
{
    delete SQ;
}

JNIEXPORT void JNICALL Java_com_project_package_JSQ_close
(JNIEnv *env, jobject, jstring jstmt, jlong shdl)
{
    status_t status;
    status = SQ->CloseSQ();

    // Java_com_project_package_JSQ_destroy(shdl);    ?
    //destroy(shdl);    ?
    return;
}

Java_com_project_package_JSQ_destroy(shdl); ?

摧毁(shdl); ?

或任何其他可以用于删除 finalize() 并找到合适的销毁位置的替代方法?

从一个本机方法调用另一个本机方法实际上是允许的。至少,我这样做时没有收到任何错误或警告。但是调用必须包含任何本机方法所期望的完整函数名称 - 即 Java_com_project_package_JSQ_destroy 并且参数应包括:

  1. JNI 环境
  2. jobject 对象
  3. 方法需要的参数。在这种情况下,shdl

因此调用必须是这样的:

Java_com_project_package_JSQ_destroy(env, <jobject_object_name>, shdl);

应该调用销毁方法。但是,它并没有真正遵守 Java 本机接口所服务的目的,本机接口是一个用于允许 Java 代码调用以其他语言编写的本机应用程序和库并被其调用的层(这里,C++)。

这样的链接是合法的。在您的特定用例中,它应该很简单。

但总的来说,有一些问题是不容忽视的:

  1. 被调用的方法需要正确的 JNI env 指针。因此,如果您从不同的线程调用它,则您有责任将它附加到 JVM。您不能跨线程传递 JNIEnv *

  2. 被调用的方法期望 JVM 在它 returns 之后立即释放所有本地引用。幸运的是,调用者可以在链接调用之前使用 PushLocalFrame()(并且不要忘记 return 上的 PopLocalFrame()

  3. 被调用的方法期望没有待处理的 JNI 异常。调用者应该通过调用 ExceptionCheck()ExceptionOccurred() 来检查它,如果它必须继续调用其他方法,也使用 ExceptionClear()。调用者在被调用者 returns.

  4. 之后检查异常是明智的