简体   繁体   English

Java:访问JNI中的静态final字段

[英]Java: access static final field in JNI

Is there a way to access a static final field in JNI? 有没有办法在JNI中访问静态最终字段?

public class TryMe {
    public int a = 1;
    public final int b = 2;
    public static int c = 3;
    public static final int d = 4;
}

The C++ JNI code: C ++ JNI代码:

jclass cls = env->FindClass("my/package/TryMe");

jfieldID a = env->GetFieldID(cls, "a", "I"); //OK
jfieldID b = env->GetFieldID(cls, "b", "I"); //OK
jfieldID c = env->GetStaticFieldID(cls, "c", "I"); //OK
jfieldID d = env->GetStaticFieldID(cls, "d", "I"); //Error!

The last call returns NULL and fails. 最后一次调用返回NULL并失败。 Any idea why? 知道为什么吗?

SOLVED: I've been bitten by proguard. 解决:我被proguard咬了。

I initially thought it might be a bug in the android runtime but it isn't. 我最初认为它可能是Android运行时中的一个错误,但事实并非如此。 I believe that proguard doesn't know and doesn't care about JNI so it decided to optimize out the "constant". 我相信proguard不知道也不关心JNI所以它决定优化“常数”。 After adding this to proguard.cfg: 将此添加到proguard.cfg后:

keep class my.package.TryMe {
    public static final int d;
}

the problem has disappeared. 问题已经消失。

After using javap -p -s TryMe , does the variable d appear like: 使用javap -p -s TryMe ,变量d看起来像:

... ...
public static final int d; public static final int d;
descriptor: I 描述符:我
... ...

?

If so, then I dont really see a reason why it shouldn't work. 如果是这样,那么我真的没有找到它不应该工作的原因。 What platform are you trying this on? 你在尝试这个平台? I tried your example with the following code on Arch x86_64 and it works for me: 我在Arch x86_64上使用以下代码尝试了您的示例,它适用于我:

Test.java: Test.java:

public class Test {
    public int a = 1;
    public final int b = 2;
    public static int c = 3;
    public static final int d = 4;
}

ctest.cpp: ctest.cpp:

#include <iostream>
#include <jni.h>

using namespace std;

int main(){
     JavaVM *jvm;
     JNIEnv *env;

    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    options[0].optionString = const_cast<char*>("-Djava.class.path=/home/fishi/Workspace/tmp/");
    vm_args.version = JNI_VERSION_1_8;
    vm_args.options = options;
    vm_args.nOptions = 1;
    vm_args.ignoreUnrecognized = JNI_FALSE;

    /* Create the Java VM */
    int res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args); 

     if (res == JNI_ERR){
         cout << "Fail: Unable to load JVM \t Exit" << endl;
        return 1;
     }
         else if (res == JNI_OK){
         cout << "CreateVM:\t\tJVM loaded successfully!" << endl;
     }

    /* Find class and static field */
    jclass cls = env->FindClass("Test");
    if(cls == NULL)
    {
        cout << "FindClass:\t\tCould not load class!" << endl;
        return 1;
    }
    else
    {
        cout << "FindClass:\t\tLoaded class successfully!" << endl;
    }
    jfieldID field = env->GetStaticFieldID(cls, "d", "I");
    if(field == NULL)
    {
        cout << "GetField:\t\tError!" << endl;
        return 1;
    }

    cout << "Success" << endl;
}

Makefile: Makefile文件:

build-jni:
    g++ -g -I/usr/lib/jvm/java-8-openjdk/include/ -I/usr/lib/jvm/java-8-openjdk/include/linux/ -L/usr/bin/java -L/usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/ ctest.cpp -ljvm -o ctest

run-jni:
    LD_LIBRARY_PATH=/usr/bin/java:/usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/ ./ctest

Output: 输出:

$ make run-jni $ make run-jni
LD_LIBRARY_PATH=/usr/bin/java:/usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/ ./ctest LD_LIBRARY_PATH = / usr / bin / java:/ usr / lib / jvm / java-8-openjdk / jre / lib / amd64 / server / ./ctest
CreateVM: JVM loaded successfully! CreateVM:JVM加载成功!
FindClass: Loaded class successfully! FindClass:已成功加载类!
Success 成功

Hope this is helpful in some way. 希望这在某些方面有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM