Skip to main content
 首页 » 编程设计

JNI学习笔记_Java调用C —— 非Android中使用的方法

2022年07月19日30mfryf

一、学习笔记


1.java源码中的JNI函数本机方法声明必须使用native修饰。

2.相对反编译 Java 的 class 字节码文件来说,反汇编.so动态库来分析程序的逻辑要复杂得多,为了应用的安全性,会将一些复杂的逻辑和
算法通过本地代码(C或C++)来实现,然后打包成.so动态库文件

3.使用了 JNI 接口的 JAVA 程序,不再像以前那样自由的跨平台。如果要实现跨平台,就必须将本地代码在不同的操作系统平台下编译出相
应的动态库。

4.JNI 开发流程主要分为以下 6 步:
(1)编写声明了 native 方法的 Java 类。
(2)将 Java 源代码编译成 class 字节码文件。
(3)用 javah -jni 命令生成.h头文件(-jni 参数表示将 class 中用native 声明的函数生成 JNI 规则的函数)。
(4)用本地代码实现.h头文件中的函数。
(5)将本地代码编译成动态库。
(6)拷贝动态库至 java.library.path 本地库搜索目录下,并运行 Java 程序。

5.JVM 查找 native 方法的两种方式:
(1)按照 JNI 规范的命名规则
(2)调用 JNI 提供的 RegisterNatives 函数,将本地函数注册到 JVM 中。

6.Native方法签名中的 JNIEXPORT 和 JNICALL 在 Linux/Unix 系统中,这两个宏可以省略不加。

7.本地实现方法的第二个参数为:如果这个 native 方法是实例方法,则该参数是 jobject,如果是静态方法,则是 jclass。

8.JNI 把 Java 中的所有对象当作一个C指针传递到本地方法中,这个指针指向 JVM 中的内部数据结构,而内部的数据结构在内存中的存储方
式是不可见的。只能从 JNIEnv 指针指向的函数表中选择合适的 JNI 函数来操作 JVM 中的数据结构。

9.JNI 的异常和 Java 中的异常处理流程是不一样的,Java 遇到异常如果没有捕获,程序会立即停止运行。而 JNI 遇到未决的异常不会改变
程序的运行流程,也就是程序会继续往下走。

10.字符串操作
(1)GetStringChars/GetStringUTFChars和ReleaseStringChars/ReleaseStringUTFChars
用于获取和释放以 Unicode/UTF-8 格式编码的字符串。后者是用于释放。
(2)GetStringLength/GetStringUTFLength
由于 UTF-8 编码的字符串以'\0'结尾(也可以使用strlen获取长度),而 Unicode 字符串不是。上面函数获取字符串长度。
(3)GetStringCritical和ReleaseStringCritical
提高 JVM 返回源字符串直接指针的可能性,但是获取和释放之间是不能阻塞的也不能调用其它JNI函数。因为获取这个直接指针后会导致暂停
GC(垃圾回收) 线程,当 GC 被暂停后,如果其它线程触发 GC 继续运行的话,都会导致被阻塞。
(4)GetStringRegion和GetStringUTFRegion
分别表示获取 Unicode 和 UTF-8 编码字符串指定范围内的内容到一个预先分配好的缓冲区中,这个函数不会分配内存,因此也没有释放的函数。

11.JNI 中的数组分为基本类型数组和对象数组,它们的处理方式是不一样的,基本类型数组中的所有元素都是 JNI 的基本数据类型,可以直
接访问。而对象数组中的所有元素是一个类的实例或其它数组的引用,和字符串操作一样,不能直接访问 Java 传递给 JNI 层的数组,必须选
择合适的 JNI 函数来访问和设置 Java 层的数组对象。

12.GC 会实时扫描所有创建的对象是否还有引用,如果没有引用则会立即清理掉。当我们创建一个像 int 数组对象的时候,当我们在本地代码
想去访问时,发现这个对象正被 GC 线程占用了,这时本地代码会一直处于阻塞状态,直到等待 GC 释放这个对象的锁之后才能继续访问。为了
避免这种现象的发生,JNI 提供了 Get/ReleasePrimitiveArrayCritical 这对函数,本地代码在访问数组对象时会暂停 GC 线程。同样这两个函
数之间不能阻塞或调用其它JNI函数

13.在 JNI 中,只有 jobject 以及子类属于引用变量,会占用引用表的空间,jint,jfloat,jboolean 等都是基本类型变量,不会占用引用
表空间,即不需要释放。引用表最大空间为 512 个,如果超出这个范围,JVM 就会挂掉。

14.本地代码调用 Java 层某个对象的方法或属性,这就是来自 C/C++层本地函数的 callback(回调)。

15.引用类型统一调用CallStaticObjectMethod 函数.(不存在CallStaticStringMethod)。

16.GetMethodID/GetStaticMethodID 和 CallIntMethod/CallStaticIntMethod 和 SetIntField/SetStaticIntField,对应static和非static成员方法的调用函数不同。

17.JNI方法签名的格式为:(形参参数类型列表)返回值。形参参数列表中,引用类型以 L 开头,后面紧跟类的全路径名(需将.全部替换成/),
以分号结尾。

18.GetMethodID()返回的jmethodID应该不是引用,不需要对它调用DeleteLocalRef()。

19.调用 GetMethodID 获取方法 ID 和调用 FindClass 获取 Class 实例后,要做异常判断

20.在 Java 中任何一个类的.class字节码文件被加载到内存中之后,该class子节码文件统一使用 Class 类来表示该类的一个引用(相当于
Java 中所有类的基类是 Object一样)。然后就可以从该类的 Class 引用中动态的获取类中的任意方法和属性。

21.在本地代码中可以调用 JNI 函数可以访问 Java 对象中的非 public(eg private) 属性和方法。

22.由于 ID 对于特定类是相同的,因此只需要查找一次,然后便可重复使用。同样,查找类对象的开销也很大,因此也应该缓存它们。

23.class 和 member id 在一定范围内是稳定的,但在动态加载的 class loader 下,保存全局的 class 要么可能失效,要么可能造成无法卸载classloader。

24.(*env)->FindClass()返回的jclass类型的变量是local referenced的,不能被缓存,因为一次JNI调用返回后它引用的已经回收了,下次在
调用JNI函数时出问题。

25.方法ID和域ID应该是可以缓存的(它不是引用,而且是稳定存在的)。

26.如果在用使用时缓存的 ID,要注意只要本地代码依赖于这个 ID 的值,那么这个类就不会被 unload。另外一方面,如果缓存发生在静态初
始化时,当类被 unload 或 reload 时,ID 会被重新计算。因此,尽量在类静态初始化时就缓存字段 ID、方法 ID。

27.有两种域ID的缓存方法:
静态缓存:在静态代码块中调用JNI native函数缓存。
使用时缓存:就是在正常调用native函数时进行缓存。

28.三种引用介绍
(1)局部引用:
通过 NewLocalRef() 和各种 JNI 接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等)。LocalRef 会阻止 GC 回收所引用
的对象。JNI函数返回后局部引用所引用的对象会被 JVM 自动释放,或在JNI函数中调用 DeleteLocalRef 释放。(*env)->DeleteLocalRef(env,local_ref)

局部引用只有在创建它的本地方法返回前有效,本地方法返回到 Java 层之后,如果 Java 层没有对return的局部引用使用的话,局部引用就会被
JVM 自动释放。在JNI函数中将局部引用存储在静态变量中缓存起来,供下次调用时使用是错误的。

JNI 会将创建的局部引用都存储在一个局部引用表中,如果这个表超过了最大容量限制,就会造成局部引用表溢出,使程序崩溃。经测试,Android 上的
JNI 局部引用表最大数量是 512 个。不用时注意及时调用DeleteLocalRef().

JNI 提供了一系列函数来管理局部引用的生命周期。这些函数包括:EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame、DeleteLocalRef。

局部引用不能跨线程使用,只在创建它的线程有效。不要试图在一个线程中创建局部引用并存储到全局引用中,然后在另外一个线程中使用。

(2)全局引用:
调用 NewGlobalRef() 基于局部引用创建,会阻止 GC 回收所引用的对象。可以跨方法、跨线程使用。JVM 不会自动释放,必须调用 DeleteGlobalRef() 手
动释放。(*env)->DeleteGlobalRef(env, g_cls_string)

只能通过 NewGlobalRef 函数创建全局引用。

(3)弱全局引用:
调用 NewWeakGlobalRef() 基于局部引用或全局引用创建,不会阻止 GC 回收所引用的对象,可以跨方法、跨线程使用。引用不会自动释放,在 JVM 认为应
该回收它的时候(比如内存紧张的时候)进行回收而被释放。或调用DeleteWeakGlobalRef() 手动释放。(*env)->DeleteWeakGlobalRef(env,g_cls_string)

29.在管理局部引用的生命周期中,Push/PopLocalFrame 是非常方便且安全的。我们可以在本地函数的入口处调用PushLocalFrame,然后在出口处调用
PopLocalFrame,这样的话,在函数内任何位置创建的局部引用都会被释放。而且,这两个函数是非常高效的,强烈建议使用它们。需要注意的是,
如果在函数的入口处调用了PushLocalFrame,记住要在函数所有出口(有 return 语句出现的地方)都要调用 PopLocalFrame。

30.JNI函数签名
JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *, jobject); //这两个宏可以忽略
JNIEnv:JNIEnv接口指针指向包含指向函数表的指针的位置。函数表中的每个条目都指向一个JNI函数。本机方法始终通过其中一个JNI函数访问Java虚拟机中的数据结构。
jobject:是对HelloWorld对象本身的引用(有点像C ++中的“this”指针),若是java中的静态native方法,参数二是jclass.

JNIEXPORT和JNICALL宏(在jni.h头文件中定义)确保从本机库导出此函数和C编译器生成具有此函数的正确调用约定的代码。
在/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/jni_md.h中(jni.h中包含它):
#define JNIEXPORT __attribute__((visibility("default")))
#define JNICALL //空宏

31.编译本地方法
$ gcc -fPIC -shared HelloWorld.c -o libHelloWorld.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
5.运行
$ export LD_LIBRARY_PATH=./ 或 setenv LD_LIBRARY_PATH . 或 不设置环境变量运行$ java -Djava.library.path=. HelloWorld
$ java HelloWorld

32.JNI以不同方式处理原始类型(java中的8种基本类型)和引用类型; 实例方法和静态方法。

33.C和C++使用JNI的区别
从jni.h中关于__cplusplus的条件编译来看,native中的类型和JNIenv中提供的函数都是不同的!
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv; //C++使用这个JNIEnv函数集合,它里面的functions域内嵌了JNINativeInterface_!
#else
typedef const struct JNINativeInterface_ *JNIEnv; //C使用这个JNIEnv函数集合
#endif

二、试验Demo

1.互传基本类型测试

/* hello_world.c */ 
#include <jni.h> 
#include <stdio.h> 
#include "com_study_jnilearn_HelloWorld.h" 
 
JNIEXPORT jint JNICALL Java_com_study_jnilearn_HelloWorld_test 
    (JNIEnv *env, jclass cls, jshort s, jint i, jlong l, jfloat f, jdouble d, jchar c, jboolean z, jbyte b) 
{ 
    jint ret = 250; 
 
    printf("C: s=%hd, i=%d, l=%ld, f=%f, d=%lf, c=%c, z=%d, b=%d\n", s, i, l, f, d, c, z, b);   
 
    return ret; 
}
/* HelloWorld.java */ 
package com.study.jnilearn;   
 
public class HelloWorld {   
 
    public static native int test(short s, int i, long l, float f, double d, char c, boolean z, byte b);   
 
    public static void main(String[] args) {   
        short s = 1;   
        long l = 20;   
        byte b = 127;   
        int ret; 
        ret = test(s, 1, l, 1.0f, 10.5, 'A', true, b); 
        System.out.println("java: ret=" + ret); 
    }   
 
    static {   
        System.loadLibrary("hello_world");   
    }   
}
编译: 
$ javac HelloWorld.java -d ./ 
$ javah -jni com.study.jnilearn.HelloWorld 
$ gcc -shared -fPIC hello_world.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libhello_world.so 
运行: 
$ java com.study.jnilearn.HelloWorld  
C: s=1, i=1, l=20, f=1.000000, d=10.500000, c=A, z=1, b=127 
java: ret=250

2.互传String类型测试

/* StringTest.java */ 
package com.study.jnilearn;   
 
public class StringTest {   
 
    public native static String sayHello(String text);   
 
    public static void main(String[] args) {   
        System.out.println("Java send: Hello, I am Java");  
        String text = sayHello("Hello, I am Java");   
        System.out.println("Java get: " + text);   
    }   
 
    static {   
        System.loadLibrary("string_test");   
    }   
}
/* string_test.c */ 
#include <stdio.h> 
#include <jni.h> 
 
JNIEXPORT jstring JNICALL Java_com_study_jnilearn_StringTest_sayHello 
    (JNIEnv *env, jclass cls, jstring j_str) 
{   
    const char *str_get = NULL;   
    const char *str_send = "Hello I am C";   
    jboolean isCopy; //返回JNI_TRUE表示原字符串的拷贝,返回JNI_FALSE表示返回原字符串的指针 
     
    str_get = (*env)->GetStringUTFChars(env, j_str, &isCopy);   
    if(str_get == NULL) {   
        return NULL;   
    }   
    printf("C get: %s, isCopy=%d\n", str_get, isCopy);   
    (*env)->ReleaseStringUTFChars(env, j_str, str_get); 
 
    printf("C send: %s\n", str_send);   
    return (*env)->NewStringUTF(env, str_send);   
}
编译: 
$ javac StringTest.java -d ./ 
$ javah -jni com.study.jnilearn.StringTest 
$ gcc -shared -fPIC string_test.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libstring_test.so 
运行: 
$ java com.study.jnilearn.StringTest 
Java send: Hello, I am Java 
C get: Hello, I am Java, isCopy=1 
C send: Hello I am C 
Java get: Hello I am C

3.基本类型数组类型双向传参

/*IntArray.java*/ 
class IntArray { 
 
    private native int[] sortArray(int[] arr); 
 
    public static void main(String[] args) { 
        IntArray obj = new IntArray(); 
        int arr1[] = new int[10]; 
 
        for (int i = 0; i < 10; i++) { 
            arr1[i] = i; 
        } 
 
        int arr2[] = obj.sortArray(arr1); 
 
        for (int i = 0; i < 10; i++) { 
            System.out.print(arr2[i] + " "); 
        } 
        System.out.println(""); 
    } 
     
    static { 
        System.loadLibrary("IntArray"); 
    } 
}
/* IntArray.c */ 
#include <jni.h> 
#include <stdio.h> 
#include <string.h> 
#include "IntArray.h" 
 
void arr_short(jint *arr, jint num) { 
    jint i, j, tmp; 
 
    for (i = 0; i < num; i++) { 
        for (j = i; j < num; j++) { 
            if (arr[i] < arr[j]) { 
                tmp = arr[i]; 
                arr[i] = arr[j]; 
                arr[j] = tmp; 
            } 
        } 
    } 
} 
 
JNIEXPORT jintArray JNICALL Java_IntArray_sortArray 
    (JNIEnv *env, jobject obj, jintArray jarr) 
{ 
    jint *array, length; 
    jint buff[64] = {0}; 
    array = (*env)->GetIntArrayElements(env, jarr, NULL); 
    if (array == NULL) { 
        return 0; /* exception occurred */ 
    } 
    length = (*env)->GetArrayLength(env, jarr); 
    memcpy(buff, array, length * sizeof(jint)); 
    (*env)->ReleaseIntArrayElements(env, jarr, array, 0); 
 
    /*不能直接传jarr然后返回jarr,因为其是一个local reference, native调用后就被回收了*/ 
    arr_short(buff, length);  
 
    jintArray new_arr = (*env)->NewIntArray(env, length);  
    (*env)->SetIntArrayRegion(env, new_arr, 0, length, buff); 
     
    return new_arr; 
}
编译: 
$ javac IntArray.java 
$ javah -jni IntArray 
$ gcc -shared -fPIC IntArray.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libIntArray.so 
运行: 
$ java IntArray  
9 8 7 6 5 4 3 2 1 0 

4.JNI函数中回调类的静态方法和实例方法

/* AccessMethod.java */ 
package com.study.jnilearn;   
 
class ClassMethod {   
 
    private static int javaStaticCallback(String str, int i) { 
        int ret = 1; 
        System.out.format("Java: javaStaticCallback: str=%s " + " i=%d\n", str, i); 
        return ret; 
    }   
 
    private int javaInstanceCallback(String str, int i) { 
        int ret = 2; 
        System.out.format("Java: javaInstanceCallback: str=%s " + "i=%d\n", str, i); 
        return ret; 
    }   
}   
 
 
public class AccessMethod {   
 
    public static native int javaStaticMethod();    
    public static native int javaInstaceMethod();   
 
    public static void main(String[] args) { 
        int ret1, ret2; 
        ret1 = javaStaticMethod();   
        ret2 = javaInstaceMethod(); 
        System.out.println("ret1=" + ret1 + " ret2=" + ret2); 
    }   
 
    static {   
        System.loadLibrary("AccessMethod");   
    }   
}
/* AccessMethod.c */ 
#include <stdio.h> 
#include "com_study_jnilearn_AccessMethod.h"   
 
JNIEXPORT jint JNICALL Java_com_study_jnilearn_AccessMethod_javaStaticMethod(JNIEnv *env, jclass cls)   
{   
    jclass clazz = NULL;   
    jstring str_arg = NULL;   
    jmethodID method_id; 
    jint ret = 0; 
 
    // 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象   
    clazz =(*env)->FindClass(env, "com/study/jnilearn/ClassMethod");   
    if (clazz == NULL) { 
        printf("Couldn't find class: com/study/jnilearn/ClassMethod");   
        return;   
    }   
 
    // 2、从clazz类中查找javaStaticCallback方法   
    method_id = (*env)->GetStaticMethodID(env, clazz, "javaStaticCallback", "(Ljava/lang/String;I)I");   
    if (method_id == NULL) {   
        printf("Couldn't find method: javaStaticCallback");   
        return;   
    }   
 
    // 3、调用clazz类的callStaticMethod静态方法   
    str_arg = (*env)->NewStringUTF(env, "C: str pass to java static method");   
    ret = (*env)->CallStaticIntMethod(env, clazz, method_id, str_arg, 111);   
 
    // 4.删除局部引用   
    (*env)->DeleteLocalRef(env, clazz);   
    (*env)->DeleteLocalRef(env, str_arg);   // id is not reference,needn't to delete 
 
    return ret; 
}   
 
 
JNIEXPORT jint JNICALL Java_com_study_jnilearn_AccessMethod_javaInstaceMethod(JNIEnv *env, jclass cls)   
{   
    jclass clazz = NULL;   
    jobject jobj = NULL;   
    jmethodID mid_construct = NULL;   
    jmethodID mid_instance = NULL;   
    jstring str_arg = NULL; 
    jint ret = 0; 
 
    // 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象   
    clazz = (*env)->FindClass(env, "com/study/jnilearn/ClassMethod");   
    if (clazz == NULL) {   
        printf("Couldn't find class: com/study/jnilearn/ClassMethod");   
        return;   
    }   
 
    // 2、获取类的默认构造方法ID   
    mid_construct = (*env)->GetMethodID(env,clazz, "<init>", "()V");   
    if (mid_construct == NULL) {   
        printf("Couldn't find construct method");   
        return;   
    }   
 
    // 3、查找实例方法的ID   
    mid_instance = (*env)->GetMethodID(env, clazz, "javaInstanceCallback", "(Ljava/lang/String;I)I");   
    if (mid_instance == NULL) {   
        return;   
    }   
 
    // 4、创建该类的实例   
    jobj = (*env)->NewObject(env, clazz, mid_construct);   
    if (jobj == NULL) {   
        printf("Couldn't create new object");   
        return;   
    }   
 
    // 5、调用对象的实例方法   
    str_arg = (*env)->NewStringUTF(env, "C: str pass to java instance method");   
    ret = (*env)->CallIntMethod(env, jobj, mid_instance, str_arg, 222);   
 
    // 删除局部引用   
    (*env)->DeleteLocalRef(env, clazz);   
    (*env)->DeleteLocalRef(env, jobj);   
    (*env)->DeleteLocalRef(env, str_arg); 
 
    return ret; 
}
编译: 
$ javac AccessMethod.java -d ./ 
$ javah -jni com.study.jnilearn.AccessMethod 
$ gcc -shared -fPIC AccessMethod.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libAccessMethod.so 
运行: 
$ java com.study.jnilearn.AccessMethod  
Java: javaStaticCallback: str=C: str pass to java static method  i=111 
Java: javaInstanceCallback: str=C: str pass to java instance method i=222 
ret1=1 ret2=2

5.JNI函数中使用静态和非静态成员属性

/* AccessField.java */ 
package com.study.jnilearn;   
 
class ClassField {   
 
    private static int num;   
    private String str;   
 
    public int getNum() {   
        return num;   
    }   
 
    public void setNum(int num) {   
        this.num = num; 
    }   
 
    public String getStr() {   
        return str;   
    }   
 
    public void setStr(String str) {   
        this.str = str;   
    }   
} 
 
public class AccessField {   
 
    private native static void accessInstanceField(ClassField obj);   
 
    private native static void accessStaticField();   
 
    public static void main(String[] args) {   
        ClassField obj = new ClassField();   
        obj.setNum(10);   
        obj.setStr("Hello");   
 
        // 本地代码访问和修改ClassField为中的静态属性num   
        accessStaticField();   
        accessInstanceField(obj);   
 
        System.out.println("Java: ClassField.num = " + obj.getNum());   
        System.out.println("Java: ClassField.str = " + obj.getStr());   
    }   
 
    static {   
        System.loadLibrary("AccessField");   
    }   
}
#include "com_study_jnilearn_AccessField.h"   
 
JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessInstanceField   
(JNIEnv *env, jclass cls, jobject obj)   
{   
    jclass clazz;   
    jfieldID fid;   
    jstring j_str;   
    jstring j_newStr;   
    const char *c_str = NULL;   
 
    // 1.获取AccessField类的Class引用   
    clazz = (*env)->GetObjectClass(env, obj);   
    if (clazz == NULL) {   
        return;   
    }   
 
    // 2. 获取AccessField类实例变量str的属性ID   
    fid = (*env)->GetFieldID(env, clazz, "str", "Ljava/lang/String;");   
    if (clazz == NULL) {   
        return;   
    }   
 
    // 3. 获取实例变量str的值   
    j_str = (jstring)(*env)->GetObjectField(env, obj, fid);   
 
    // 4. 将unicode编码的java字符串转换成C风格字符串   
    c_str = (*env)->GetStringUTFChars(env, j_str, NULL);   
    if (c_str == NULL) {   
        return;   
    }   
    printf("C: ClassField.str = %s\n", c_str);   
    (*env)->ReleaseStringUTFChars(env, j_str, c_str);   
 
    // 5. 修改实例变量str的值   
    j_newStr = (*env)->NewStringUTF(env, "World");   
    if (j_newStr == NULL) {   
        return;   
    }   
 
    (*env)->SetObjectField(env, obj, fid, j_newStr);   
 
    // 6.删除局部引用   
    (*env)->DeleteLocalRef(env, clazz);   
    (*env)->DeleteLocalRef(env, j_str);   
    (*env)->DeleteLocalRef(env, j_newStr);   
}   
 
JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessStaticField   
(JNIEnv *env, jclass cls)   
{   
    jclass clazz;   
    jfieldID fid;   
    jint num;   
 
    // 1.获取ClassField类的Class引用   
    clazz = (*env)->FindClass(env, "com/study/jnilearn/ClassField");   
    if (clazz == NULL) { 
        return;   
    }   
 
    // 2.获取ClassField类静态变量num的属性ID   
    fid = (*env)->GetStaticFieldID(env, clazz, "num", "I");   
    if (fid == NULL) {   
        return;   
    }   
 
    // 3.获取静态变量num的值   
    num = (*env)->GetStaticIntField(env, clazz, fid);   
    printf("C: ClassField.num = %d\n", num);   
 
    // 4.修改静态变量num的值   
    (*env)->SetStaticIntField(env, clazz, fid, 88);   
 
    // 删除属部引用   
    (*env)->DeleteLocalRef(env, clazz);   
}
编译: 
$ javac AccessField.java -d ./ 
$ javah -jni com.study.jnilearn.AccessField 
$ gcc -shared -fPIC AccessField.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libAccessField.so 
运行: 
$ java com.study.jnilearn.AccessField 
C: ClassField.num = 10 
C: ClassField.str = Hello 
Java: ClassField.num = 88 
Java: ClassField.str = World

6.优化:类静态初始化缓存

/* AccessCache.java */ 
public class AccessCache { 
 
    public static native void initIDs();  
 
    public native void nativeMethod(); 
    public void callback() { 
        System.out.println("Java: AccessCache.callback invoked!"); 
    } 
 
    public static void main(String[] args) { 
        AccessCache accessCache = new AccessCache(); 
        accessCache.nativeMethod(); 
    } 
 
    static { 
        System.loadLibrary("AccessCache"); 
        initIDs(); 
    } 
}
#include <stdio.h> 
#include "AccessCache.h" 
 
jmethodID MID_AccessCache_callback; 
 
JNIEXPORT void JNICALL Java_AccessCache_initIDs(JNIEnv *env, jclass cls) 
{ 
    printf("initIDs called!\n"); 
    MID_AccessCache_callback = (*env)->GetMethodID(env, cls, "callback", "()V"); 
} 
 
JNIEXPORT void JNICALL Java_AccessCache_nativeMethod(JNIEnv *env, jobject obj) 
{ 
    printf("C: call java's callback()\n"); 
    (*env)->CallVoidMethod(env, obj, MID_AccessCache_callback); 
}
编译: 
javac AccessCache.java 
javah -jni AccessCache 
$ gcc -shared -fPIC AccessCache.c -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o libAccessCache.so 
运行: 
$ java AccessCache  
initIDs called! 
C: call java's callback() 
Java: AccessCache.callback invoked!

jni.pdf翻译总结版:http://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/recommend.html


本文参考链接:https://www.cnblogs.com/hellokitty2/p/10768883.html