JNI 开发流程主要分为以下 6 步:
编写声明了 native 方法的 Java 类
将 Java 源代码编译成 class 字节码文件
用 javah -jni 命令生成 .h 头文件(javah 是 jdk 自带的一个命令,-jni 参数表示将 class 中用native 声明
的函数生成 JNI 规则的函数)
用本地代码实现 .h头 文件中的函数
将本地代码编译成动态库(Windows:\*.dll,linux/unix:\*.so,mac os x:\*.jnilib)
拷贝动态库至 java.library.path 本地库搜索目录下,并运行 Java 程序
MainActivity类
  static {
        System.loadLibrary("HelloWorld");//引入so包
    }
    public static native String sayHi();//调用c/c++



//生成h头文件
1.先选中java目录,然后右击open in ,选 Terminal。也就是进入java目录输入命令
javah -classpath . -jni com.example.myapplication.MainActivity


//新建一个cpp文件 把头文件复制粘贴进来
JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_sayHi
        (JNIEnv *env, jclass){
    return env->NewStringUTF("from c语言");
};

//生成动态库方式 
1) cmake
2)  ndk-build 
下面我用得是cmake


CMakeLists.txt
cmake_minimum_required(VERSION 3.18.1)
project("HelloWorld")
add_library( # Sets the name of the library.
        HelloWorld
        SHARED
        native-lib.cpp)
find_library( # Sets the name of the path variable.
        log-lib
        log)
target_link_libraries( # Specifies the target library.
        HelloWorld
        ${log-lib})


app.gradle
defaultConfig {
       ...
        externalNativeBuild {
            cmake {
                cppFlags ''
            }
        }
 }
 externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.18.1'
        }
 }

2.传参数给c/c++和返回

MainActivity类
public static native String sayHi(String name);


/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    sayHi
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_sayHi
        (JNIEnv *env, jclass jclass, jstring jstring){
    //定义变量
    const char *c_str=NULL;
    char buff[128] ={0};
    //jstring转char
    c_str = (*env).GetStringUTFChars(jstring, NULL);
    if (c_str == NULL)
    {
        return NULL;
    }
    //释放jstring指向Char的指针
    (*env).ReleaseStringUTFChars(jstring, c_str);
    //发送格式化输出到buff所指向的字符串(c_str)
    sprintf(buff, "hello %s", c_str);
    return (*env).NewStringUTF(buff);
}

3.数据类型对应(注意类型之间的对应)

MainActivity类
 public static native void myTest(short s, int i, long l, float f, double d, char c,
                                   boolean z, byte b, String str, Object obj, Student p, int[] arr);


C文件
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_myTest
        (JNIEnv *env, jclass cls, jshort s, jint i, jlong l, jfloat f,
         jdouble d, jchar c, jboolean z, jbyte b, jstring j_str, jobject jobj1, jobject job2, jintArray j_int_arr)
        {
            printf("s=%hd, i=%d, l=%ld, f=%f, d=%lf, c=%c, z=%c, b=%d", s, i, l, f, d, c, z, b);
            const char *c_str = NULL;
            c_str = (*env).GetStringUTFChars(j_str,NULL);
            if (c_str == NULL)
            {
                return; // memory out
            }
            (*env).ReleaseStringUTFChars(j_str, c_str);
            printf("c_str: %s\n", (char*)c_str);
        }
#ifdef __cplusplus

1.返回字符串

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("HelloWorld");
    }
    public static native String sayHi(String name);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView=findViewById(R.id.tv_hi);
        textView.setText(sayHi("gu"));
    }
}

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    sayHi
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_sayHi
        (JNIEnv *env, jclass jclass, jstring jstring){
    //定义变量
    const char *c_str=NULL;
    char buff[128] ={0};
    //jstring拷贝
    c_str = (*env).GetStringUTFChars(jstring, NULL);
    if (c_str == NULL)
    {
        return NULL;
    }
    //发送格式化输出到buff所指向的字符串(c_str)
    sprintf(buff, "hello %s", c_str);
    //释放jstring指向Char的指针
    (*env).ReleaseStringUTFChars(jstring, c_str);
    return (*env).NewStringUTF(buff);
}
#ifdef __cplusplus
}
#endif
#endif






错误用法:
JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_sayHi
        (JNIEnv *env, jclass jclass, jstring jstring){
    //定义变量
    const jchar *c_str = NULL;
    char buff[128] = "hello";
    char *pBuff = buff + 6;
    /*
* 在GetStringCritical/RealeaseStringCritical之间是一个关键区。
* 在这关键区之中,绝对不能呼叫JNI的其他函数和会造成当前线程中断或是会让当前线程等待的任何本地代码,
* 否则将造成关键区代码执行区间垃圾回收器停止运作,任何触发垃圾回收器的线程也会暂停。
* 其他触发垃圾回收器的线程不能前进直到当前线程结束而激活垃圾回收器。
*/
    c_str=(*env).GetStringCritical(jstring,NULL); 返回源字符串指针的可能性
    if (c_str ==NULL){
        return NULL;
    }
    while (*c_str)
    {
        *pBuff++=*c_str++;
    }
    (*env).ReleaseStringCritical(jstring,c_str);
    return (*env).NewStringUTF(buff);
}



#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    sayHi
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_sayHi
        (JNIEnv *env, jclass jclass, jstring jstring){
    jsize len=(*env).GetStringLength(jstring);
    char buff[128]="hello ";
    char* pBuff=buff+6;
    // 将JVM中的字符串以utf-8编码拷入C缓冲区,该函数内部不会分配内存空间
    (*env).GetStringUTFRegion(jstring, 0, len, pBuff);
    return (*env).NewStringUTF(buff);
}
#ifdef __cplusplus
}
#endif
#endif

2.返回数组

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("HelloWorld");
    }
    private native int sumArray(int[] arr);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = findViewById(R.id.tv_hi);

        int[] arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
        int sum = sumArray(arr);
        textView.setText("返回=="+sum);
    }
}

第一种
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    sayHi
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jint JNICALL Java_com_example_myapplication_MainActivity_sumArray
        (JNIEnv *env, jobject obj, jintArray j_array){
    jint i, sum = 0;
    jint *c_array;
    jint arr_len;
// 可能数组中的元素在内存中是不连续的,JVM可能会复制所有原始数据到缓冲区,然后返回这个缓冲区的指针
    c_array = (*env).GetIntArrayElements(j_array,NULL);
    if (c_array == NULL) {
        return 0; // JVM复制原始数据到缓冲区失败
    }
    arr_len = (*env).GetArrayLength(j_array);
    printf("arr_len = %d\n", arr_len);
    for (i = 0; i < arr_len; i++) {
        sum += c_array[i];
    }
    (*env).ReleaseIntArrayElements(j_array, c_array, 0); // 释放可能复制的缓冲区
    return sum;
}
#ifdef __cplusplus
}
#endif
#endif


第二种
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    sayHi
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jint JNICALL Java_com_example_myapplication_MainActivity_sumArray
        (JNIEnv *env, jobject obj, jintArray j_array){
    jint  i,sum=0;
    jint *c_array;
    jint arr_len;
    //1. 获取数组长度
    arr_len=(*env).GetArrayLength(j_array);
    //2. 根据数组长度和数组元素的数据类型申请存放java数组元素的缓冲区
    c_array=  (jint*)malloc(sizeof(jint)*arr_len);
    //3. 初始化缓冲区
    memset(c_array,0,sizeof(jint)*arr_len);
    //4. 拷贝Java数组中的所有元素到缓冲区中
    (*env).GetIntArrayRegion(j_array,0,arr_len,c_array);
    for ( i = 0; i <arr_len ; ++i) {
        sum+=c_array[i];//5. 累加数组元素的和
    }
    free(c_array); //6. 释放存储数组元素的缓冲区
    return sum;
}
#ifdef __cplusplus
}
#endif
#endif

二维数组

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    sayHi
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jobjectArray JNICALL Java_com_example_myapplication_MainActivity_initInt2DArray
        (JNIEnv *env, jobject obj, jint size)
        {
            jobjectArray result;
            jclass clsIntArray;
            jint i,j;
     // 1.获得一个int型二维数组类的引用
    clsIntArray = (*env).FindClass("[I");
    if (clsIntArray == NULL)
    {
        return NULL;
    }
// 2.创建一个数组对象(里面每个元素用clsIntArray表示)
    result = (*env).NewObjectArray(size,clsIntArray,NULL);
    if (result == NULL)
    {
        return NULL;
    }
// 3.为数组元素赋值
    for (i = 0; i < size; ++i)
    {
        jint buff[256];
        jintArray intArr = (*env).NewIntArray(size);//创建一个 JNI 的 int 数组
        if (intArr == NULL)
        {
            return NULL;
        }
        for (j = 0; j < size; j++)
        {
            buff[j] = i + j;
        }
        //buff[]缓冲中的内容复制到新分配的一维数组中去
        (*env).SetIntArrayRegion(intArr, 0,size,buff);
        //在外层循环中依次将 int[]数组赋值到 jobjectArray 数组中
        (*env).SetObjectArrayElement(result, i, intArr);
        //DeleteLocalRef 将新创建的 jintArray 引用从引用表中移除
        (*env).DeleteLocalRef(intArr);
    }
    return result;
}



#ifdef __cplusplus
}
#endif
#endif


下面是java
public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("HelloWorld");
    }
    private native int[][] initInt2DArray(int size);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = findViewById(R.id.tv_hi);

        int[][] arr = initInt2DArray(3);
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                System.out.format("arr[%d][%d] = %d\n", i, j, arr[i][j]);
            }
        }
        textView.setText("返回==");
    }
}

CC++访问 Java 实例方法和静态方法

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("HelloWorld");
    }
    public static native void callJavaStaticMethod();
    public static native void callJavaInstaceMethod();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        callJavaStaticMethod();
        callJavaInstaceMethod();
    }
}

public class ClassMethod {
    private static void callStaticMethod(String str, int i) {
        System.out.format("ClassMethod::callStaticMethod called!-->str=%s," +
                " i=%d\n", str, i);
    }
    private void callInstanceMethod(String str, int i) {
        System.out.format("ClassMethod::callInstanceMethod called!-->str=%s, " +
                "i=%d\n", str, i);
    }
}



/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    callJavaStaticMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_callJavaStaticMethod
        (JNIEnv *env, jclass cls){
    jclass clazz = NULL;
    jstring str_arg = NULL;
    jmethodID mid_static_method;
// 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象
    clazz =(*env).FindClass("com/example/myapplication/ClassMethod");
    if (clazz == NULL) {
        return;
    }
// 2、从clazz类中查找callStaticMethod方法
    mid_static_method = (*env).GetStaticMethodID(clazz,"callStaticMethod","(Ljava/lang/String;I)V");
    if (mid_static_method == NULL) {
        printf("找不到callStaticMethod这个静态方法。");
        return;
    }
// 3、调用clazz类的callStaticMethod静态方法
    str_arg = (*env).NewStringUTF("我是静态方法");
    (*env).CallStaticVoidMethod(clazz,mid_static_method, str_arg, 100);
// 删除局部引用
    (*env).DeleteLocalRef(clazz);
    (*env).DeleteLocalRef(str_arg);
}

/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    callJavaInstaceMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_callJavaInstaceMethod
        (JNIEnv *env, jclass cls){
    jclass clazz = NULL;
    jobject jobj = NULL;
    jmethodID mid_construct = NULL;
    jmethodID mid_instance = NULL;
    jstring str_arg = NULL;
// 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象
    clazz = (*env).FindClass("com/example/myapplication/ClassMethod");
    if (clazz == NULL) {
        printf("找不到'com.study.jnilearn.ClassMethod'这个类");
        return;
    }
// 2、获取类的默认构造方法ID
    mid_construct = (*env).GetMethodID(clazz, "<init>","()V");
    if (mid_construct == NULL) {
        printf("找不到默认的构造方法");
        return;
    }
// 3、查找实例方法的ID
    mid_instance = (*env).GetMethodID( clazz, "callInstanceMethod", "(Ljava/lang/String;I)V");
    if (mid_instance == NULL) {
        return;
    }
// 4、创建该类的实例
    jobj = (*env).NewObject(clazz,mid_construct);
    if (jobj == NULL) {
        printf("在com.study.jnilearn.ClassMethod类中找不到callInstanceMethod方法");
        return;
    }
// 5、调用对象的实例方法
    str_arg = (*env).NewStringUTF("我是实例方法");
    (*env).CallVoidMethod(jobj,mid_instance,str_arg,200);
// 删除局部引用
    (*env).DeleteLocalRef(clazz);
    (*env).DeleteLocalRef(jobj);
    (*env).DeleteLocalRef(str_arg);
};

#ifdef __cplusplus
}
#endif
#endif

CC++ 访问 Java 实例变量和静态变量

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("HelloWorld");
    }
    private native static void accessInstanceField(ClassField obj);
    private native static void accessStaticField();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ClassField obj = new ClassField();
        obj.setNum(10);
        obj.setStr("Hello");
// 本地代码访问和修改ClassField为中的静态属性num
        accessStaticField();
        accessInstanceField(obj);
// 输出本地代码修改过后的值
        System.out.println("In Java--->ClassField.num = " + obj.getNum());
        System.out.println("In Java--->ClassField.str = " + obj.getStr());
    }
}

public class ClassField {
    private static int num;
    private String str;
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        ClassField.num = num;
    }
    public String getStr() {
        return str;
    }
    public void setStr(String str) {
        this.str = str;
    }
}



/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    accessInstanceField
 * Signature: (Lcom/example/myapplication/ClassField;)V
 */
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_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(obj);
    if (clazz == NULL) {
        return;
    }
    // 2. 获取AccessField类实例变量str的属性ID
    fid = (*env).GetFieldID(clazz,"str", "Ljava/lang/String;");
    if (clazz == NULL) {
        return;
    }
// 3. 获取实例变量str的值
    j_str = (jstring)(*env).GetObjectField(obj,fid);
// 4. 将unicode编码的java字符串转换成C风格字符串
    c_str = (*env).GetStringUTFChars(j_str,NULL);
    if (c_str == NULL) {
        return;
    }
    printf("In C--->ClassField.str = %s\n", c_str);//没有打印  In c--->ClassField.str=Hello
    (*env).ReleaseStringUTFChars( j_str, c_str);
// 5. 修改实例变量str的值
    j_newStr = (*env).NewStringUTF("This is C String");
    if (j_newStr == NULL) {
        return;
    }
    (*env).SetObjectField( obj, fid, j_newStr);
// 6.删除局部引用
    (*env).DeleteLocalRef( clazz);
    (*env).DeleteLocalRef( j_str);
    (*env).DeleteLocalRef( j_newStr);
};

/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    accessStaticField
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_accessStaticField
        (JNIEnv *env, jclass cls){
    jclass clazz;
    jfieldID fid;
    jint num;
//1.获取ClassField类的Class引用
    clazz = (*env).FindClass("com/example/myapplication/ClassField");
    if (clazz == NULL) { // 错误处理
        return;
    }
//2.获取ClassField类静态变量num的属性ID
    fid = (*env).GetStaticFieldID( clazz, "num", "I");
    if (fid == NULL) {
        return;
    }
// 3.获取静态变量num的值
    num = (*env).GetStaticIntField(clazz,fid);
    printf("In C--.ClassField.num = %d\n", num);//没有打印 In c--->ClassField.num=10
// 4.修改静态变量num的值
    (*env).SetStaticIntField( clazz, fid, 80);
// 删除属部引用
    (*env).DeleteLocalRef(clazz);
};

#ifdef __cplusplus
}
#endif
#endif

构造方法和父类实例方法

public class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
        System.out.println("Animal Construct call...");
    }
    public String getName() {
        System.out.println("Animal.getName Call...");
        return this.name;
    }
    public void run() {
        System.out.println("Animal.run...");
    }
}

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
        System.out.println("Cat Construct call....");
    }
    @Override
    public String getName() {
        return "My name is " + this.name;
    }
    @Override
    public void run() {
        System.out.println(name + " Cat.run...");
    }
}


public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("HelloWorld");
    }
    public native static void callSuperInstanceMethod();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        callSuperInstanceMethod();
    }
}



/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    callSuperInstanceMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_callSuperInstanceMethod
        (JNIEnv *env, jclass cls){
    jclass cls_cat;
    jclass cls_animal;
    jmethodID mid_cat_init;
    jmethodID mid_run;
    jmethodID mid_getName;
    jstring c_str_name;
    jobject obj_cat;
    const char *name = NULL;
// 1、获取Cat类的class引用 com.example.myapplication
    cls_cat = (*env).FindClass( "com/example/myapplication/Cat");
    if (cls_cat == NULL) {
        return;
    }
// 2、获取Cat的构造方法ID(构造方法的名统一为:<init>)
    mid_cat_init = (*env).GetMethodID(cls_cat, "<init>", "(Ljava/lang/String;)V");
    if (mid_cat_init == NULL) {
        return; // 没有找到只有一个参数为String的构造方法
    }
// 3、创建一个String对象,作为构造方法的参数
    c_str_name = (*env).NewStringUTF("汤姆猫");
    if (c_str_name == NULL) {
        return; // 创建字符串失败(内存不够)
    }
    // 4、创建Cat对象的实例(调用对象的构造方法并初始化对象)
    obj_cat = (*env).NewObject(cls_cat, mid_cat_init,c_str_name);
    if (obj_cat == NULL) {
        return;
    }
//-------------- 5、调用Cat父类Animal的run和getName方法 --------------
    cls_animal = (*env).FindClass("com/example/myapplication/Animal");
    if (cls_animal == NULL) {
        return;
    }
// 例1: 调用父类的run方法
    mid_run = (*env).GetMethodID(cls_animal, "run", "()V"); // 获取父类Animal中run方法的id
    if (mid_run == NULL) {
        return;
    }
// 注意:obj_cat是Cat的实例,cls_animal是Animal的Class引用,mid_run是Animal类中的方法ID
    (*env).CallNonvirtualVoidMethod(obj_cat, cls_animal, mid_run);
// 例2:调用父类的getName方法
// 获取父类Animal中getName方法的id
    mid_getName = (*env).GetMethodID(cls_animal, "getName", "()Ljava/lang/String;");
    if (mid_getName == NULL) {
        return;
    }
    c_str_name = static_cast<jstring>((*env).CallNonvirtualObjectMethod(obj_cat, cls_animal,
                                                                        mid_getName));
    name = (*env).GetStringUTFChars(c_str_name, NULL);
    printf("In C: Animal Name is %s\n", name);
// 释放从java层获取到的字符串所分配的内存
    (*env).ReleaseStringUTFChars(c_str_name, name);
    quit:
// 删除局部引用(jobject或jobject的子类才属于引用变量),允许VM释放被局部变量所引用的资源
    (*env).DeleteLocalRef(cls_cat);
    (*env).DeleteLocalRef(cls_animal);
    (*env).DeleteLocalRef(c_str_name);
    (*env).DeleteLocalRef(obj_cat);
}
#ifdef __cplusplus
}
#endif
#endif

使用时缓存

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("HelloWorld");
    }
    private String str = "Hello";
    public native void accessField();
    public native String newString(char[] chars, int len);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        accessField();
        char chars[] = new char[7];
        chars[0] = '1';
        chars[1] = '2';
        chars[2] = '3';
        chars[3] = '4';
        chars[4] = '5';
        chars[5] = '6';
        chars[6] = '7';
        String str = newString(chars, 6);
        System.out.println(str);
    }
}



/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    accessField
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_accessField
        (JNIEnv *env, jobject obj)
{
// 第一次访问时将字段存到内存数据区,直到程序结束才会释放,可以起到缓存的作用
    static jfieldID fid_str = NULL;
    jclass cls_AccessCache;
    jstring j_str;
    const char *c_str;
    cls_AccessCache = (*env).GetObjectClass(obj); // 获取该对象的Class引用
    if (cls_AccessCache == NULL) {
        return;
    }
// 先判断字段ID之前是否已经缓存过,如果已经缓存过则不进行查找
    if (fid_str == NULL) {
        fid_str = (*env).GetFieldID(cls_AccessCache,"str","Ljava/lang/String;");
        // 再次判断是否找到该类的str字段
        if (fid_str == NULL) {
            return;
        } }
    j_str = static_cast<jstring>((*env).GetObjectField(obj, fid_str)); // 获取字段的值
    c_str = (*env).GetStringUTFChars(j_str, NULL);
    if (c_str == NULL) {
        return; // 内存不够
    }
    printf("In C:\n str = \"%s\"\n", c_str);
    (*env).ReleaseStringUTFChars( j_str, c_str); // 释放从从JVM新分配字符串的内存空间
// 修改字段的值
    j_str = (*env).NewStringUTF( "12345");
    if (j_str == NULL) {
        return;
    }
    (*env).SetObjectField( obj, fid_str, j_str);
// 释放本地引用
    (*env).DeleteLocalRef(cls_AccessCache);
    (*env).DeleteLocalRef(j_str);
}

/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    newString
 * Signature: ([CI)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_newString
        (JNIEnv *env, jobject obj, jcharArray j_char_arr, jint len)
{
    jcharArray elemArray;
    jchar *chars = NULL;
    jstring j_str = NULL;
    static jclass cls_string = NULL;
    static jmethodID cid_string = NULL;
// 注意:这里缓存局引用的做法是错误,这里做为一个反面教材提醒大家,下面会说到。
    if (cls_string == NULL) {
        cls_string = (*env).FindClass("java/lang/String");
        if (cls_string == NULL) {
            return NULL;
        } }
// 缓存String的构造方法ID
    if (cid_string == NULL) {
        cid_string = (*env).GetMethodID(cls_string, "<init>", "([C)V");
        if (cid_string == NULL) {
            return NULL;
        } }
    printf("In C array Len: %d\n", len);
// 创建一个字符数组
    elemArray = (*env).NewCharArray(len);
    if (elemArray == NULL) {
        return NULL;
    }
// 获取数组的指针引用,注意:不能直接将jcharArray作为SetCharArrayRegion函数最后一个参数
    chars = (*env).GetCharArrayElements(j_char_arr,NULL);
    if (chars == NULL) {
        return NULL;
    }
// 将Java字符数组中的内容复制指定长度到新的字符数组中
    (*env).SetCharArrayRegion(elemArray, 0, len, chars);
// 调用String对象的构造方法,创建一个指定字符数组为内容的String对象
    j_str = static_cast<jstring>((*env).NewObject(cls_string, cid_string, elemArray));
// 释放本地引用
    (*env).DeleteLocalRef(elemArray);
    return j_str;
}

#ifdef __cplusplus
}
#endif
#endif

类静态初始化缓存

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("HelloWorld");
        initIDs();
    }
    public static native void initIDs();
    public native void nativeMethod();
    public void callback() {
        System.out.println("AccessCache.callback invoked!");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
      nativeMethod();
    }
}



/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <cstdio>
/* Header for class com_example_myapplication_MainActivity */

#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
jmethodID MID_AccessCache_callback;
/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    initIDs
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_initIDs
        (JNIEnv *env, jclass cls)
{
    printf("initIDs called!!!\n");
    MID_AccessCache_callback = (*env).GetMethodID(cls,"callback","()V");
}

/*
 * Class:     com_example_myapplication_MainActivity
 * Method:    nativeMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_nativeMethod
        (JNIEnv *env, jobject obj) {
    printf("In C Java_com_study_jnilearn_AccessCache_nativeMethod called!!!\n");
    (*env).CallVoidMethod(obj, MID_AccessCache_callback);
}

#ifdef __cplusplus
}
#endif
#endif
JNI 局部引用、全局引用和弱全局引用
Logo

更多推荐