从JNI调用静态void Java方法

4
我的C代码无法找到我在Java中的公共静态函数调用。它可以无误地找到类。
我试图将结果返回给回调方法onResponse。稍后会用更复杂的内容替换"5"。
我在StackOverflow上看到了类似的问题,但迄今为止没有任何改变似乎有所帮助。也许我有逻辑错误?
我的JNI(已更新#2):
void cec_debug_msg(JNIEnv *env, cec_rx_message_t* msg) {
    jclass cls = (*env)->FindClass(env, "com/example/utils/CECUtils");
    if(!cls) {
        LOGE("Could not find the CEC class.");
    } else {
        jmethodID methodid = (*env)->GetStaticMethodID(env, cls, "onResponse", "(I)V");
        if(!methodid) {
            // Code always reaches this point, never finding the method
            LOGE("Could not find the callback method.");
        } else {
            LOGV("Called static void method.");
            (*env)->CallStaticVoidMethod(env, cls, methodid, 5);
        }
    }
}

这段代码包含在一个方法中,该方法从这个函数调用:
JNIEXPORT int JNICALL Java_com_example_utils_CECUtils_startListening(JNIEnv *env, jclass cls) {

    ...

    cec_debug_msg(env, &rx_msg);

    ...

}

我的Java(更新#1):

public class CECUtils {

    static {
        System.loadLibrary("cecutils");
    }

    public static native int startListening();

    ...

    public static void onResponse(int opcode) {
        Utils.log("CEC", "From JNI!");
    }

}

签名检查:

javap -s -p CECUtils

public static void onResponse(int);

签名:(I)V

解决方案:检查ProGuard!

-keep public class com.example.utils.CECUtils {
    *;
}

ProGuard 是我的问题所在。代码在调试时运行良好,但发布版本却将 Java 类压缩,JNI 无法再找到方法。我在此添加我的错误以帮助未来的搜索:Caused by: java.lang.NoSuchMethodError: no non-static method "Lcom/domain/path/to/MyClass;.javaFuncCalledFromJNI(Ljava/lang/String;)V" - Al.
2个回答

7

谢谢,不幸的是我也有同样的问题。我会看一下你的链接。 - Knossos
我添加了更能反映我的实际代码的代码,只是为了更完整。 - Knossos
错误只是ProGuard引起的。我没有添加异常。将您的评论设置为答案,因为您捕捉到了我的初始错误。 - Knossos

5
请参考方法签名(http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/method.html),以下是需要翻译的内容:

jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V"); 其中 (I)V 确定了方法签名。

下面是 GetMethodID 的文档:

GetMethodID

jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

返回类或接口的实例(非静态)方法的方法 ID。该方法可能在 clazz 的超类中定义并被 clazz 继承。该方法由其名称和签名确定。

GetMethodID() 导致未初始化的类被初始化。

要获取构造函数的方法 ID,请将名称指定为 <init>,返回类型指定为 void (V)。

您可以使用 javap 来确定方法的签名:

Using javap to Generate Method Signatures

To eliminate the mistakes in deriving method signatures by hand, you can use the javap tool to print out method signatures. For example, by running:

javap -s -p Prompt

you can obtain the following output:

Compiled from Prompt.java
class Prompt extends java.lang.Object 
    /* ACC_SUPER bit set */
{
    private native getLine (Ljava/lang/String;)Ljava/lang/String;
    public static main ([Ljava/lang/String;)V
    <init> ()V
    static <clinit> ()V
}
“-s”标志告知javap输出签名,而不是普通的Java类型。“-p”标志会包含私有成员。”

签名匹配: public static void onResponse(int); Signature: (I)V - Knossos
rx_msg 可以忽略不计。主要是要检测到 onResponse 方法。 - Knossos
错误就是ProGuard。我忘记添加一个例外。 - Knossos
在其声明中,onResponse(int opcode) 给出了变量 int opcode。当查看 https://dev59.com/ln3aa4cB1Zd3GeqPdG9I 时,JNI 必须分配字段,因此也必须分配在 Java 环境中的 int opcode 字段。或许 JNI 无法在 Java 内存空间中分配 int opcode 字段,因为它没有被声明给 JNI? - ralf htp
这些是最难搞的!我花了一个小时才弄清楚ufw会丢弃我的UDP数据包(IEEE 802.15.4设备)。 - ralf htp
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接