module里面

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.joyy.nativecpp"
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        // 指定c语言标准
        externalNativeBuild {
            cmake {
                cppFlags '-std=c++11'
            }
            ndk{
                abiFilters "armeabi-v7a"
            }
        }
        // 指定存放so库的路径
        sourceSets{
            main{
                jniLibs.srcDirs=['libs']
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    // 指定CMakeList的位置
    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.10.2'
        }
    }
    buildFeatures {
        viewBinding true
    }
}

JNI常见用法

https://www.jianshu.com/p/6cbdda111570

Java和JNI基本类型对照表

音视频-java和jni相互调用_Java

Java与JNI引用类型对照表

音视频-java和jni相互调用_对象引用_02

字符串操作

extern "C"
JNIEXPORT void JNICALL
xxxxxxxxxx(JNIEnv *env, jobject instance, jstring str_) {
	// 生成JNIString
	char const * str = "hello world!";
	jstring jstring = env->NewStringUTF(str);
	
	// jstring转换成const char* charstr
	const char * charstr = env->GetStringUTFChars(str_, 0);
	
	//释放const char *
	env->ReleaseStringUTFChars(str_, charstr);
	
	//获取字符串子集
	char * subStr = new char;
	env->GetStringUTFRegion(str_, 0, 3, subStr);//截取字符串char*;
	
	env->ReleaseStringUTFChars(str_, subStr);
}

数组操作

// 整型数组代码
public native void testIntArray(int []array);
// Object Array相关代码
public native void testObjectArray(String[] strArr);

extern "C"
JNIEXPORT void JNICALL
Java_com_joyy_nativecpp_MainActivity_testIntArray(JNIEnv *env, jobject thiz, jintArray array_) {
    // ---- 获取数组元素
    // (1)获取数组中元素
    jint *intArray = env->GetIntArrayElements(array_, nullptr);
    int len = env->GetArrayLength(array_); //(2)获取数组长度
    XLOGI("len:%d", len);
    for (int i = 0; i < len; i++) {
        jint item = intArray[i];
        XLOGI("item[%d]:%d", i, item);
    }
    env->ReleaseIntArrayElements(array_, intArray, 0);

    // ---- 获取子数组
    jint *subArray = new jint;
    env->GetIntArrayRegion(array_, 0, 3, subArray);
    for (int i = 0; i < 3; i++) {
        subArray[i] = subArray[i] + 5;
        XLOGI("subArrya:[%d]:%d", i, subArray[i]);
    }

    // 用子数组修改原数组元素
    env->SetIntArrayRegion(array_, 0, 3, subArray);
    env->ReleaseIntArrayElements(array_, subArray, 0);//释放子数组元素
}
extern "C"
JNIEXPORT void JNICALL
Java_com_joyy_nativecpp_MainActivity_testObjectArray(JNIEnv *env, jobject thiz,
                                                     jobjectArray str_arr) {
    // 获取数组长度
    int len = env->GetArrayLength(str_arr);
    for (int i = 0; i < len; i++) {
        //获取Object的数组元素
        jstring item = (jstring) env->GetObjectArrayElement(str_arr, i);

        const char *charStr = env->GetStringUTFChars(item, 0);
        XLOGI("strArray item:%s", charStr);

        jstring jresult = env->NewStringUTF("HaHa");
        // 设置Object数组元素
        env->SetObjectArrayElement(str_arr, i, jresult);
        env->ReleaseStringUTFChars(item, charStr);
    }
}

JNI访问Java类的方法和

音视频-java和jni相互调用_c++_03

3、JNI 访问Java类的方法和字段

// jni调用Java对象方法
    public native void testCallJavaMethod();

    // jni调用java static方法
    public native void testCallStaticJavaMethod();

    // jni访问java的对象属性和类属性
    public native void getJavaObjectField(Student student);

    public void helloworld(String msg) {
        Log.d("XPlay", "\n\n");
        Log.d("XPlay", "hello world:" + msg);
    }

    public static void helloworldStatic(String msg) {
        Log.d("XPlay", "\n\n");
        Log.d("XPlay", "Static hellow world:" + msg);
    }




extern "C"
JNIEXPORT void JNICALL
Java_com_joyy_nativecpp_MainActivity_testCallJavaMethod(JNIEnv *env, jobject thiz) {
    // 获取类名
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) return;

    jmethodID javaMethod = env->GetMethodID(clazz, "helloworld", "(Ljava/lang/String;)V");
    if (javaMethod == NULL) return;
    const char *msg = "nancy";
    jstring jmsg = env->NewStringUTF(msg);
    env->CallVoidMethod(thiz, javaMethod, jmsg);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_joyy_nativecpp_MainActivity_testCallStaticJavaMethod(JNIEnv *env, jobject thiz) {
    // 获取java类型
    jclass  clazz = env->GetObjectClass(thiz);
    if(clazz == NULL) return;

    jmethodID staticMethod = env->GetStaticMethodID(clazz, "helloworldStatic", "(Ljava/lang/String;)V");
    if(staticMethod == NULL) return;

    jstring  jmsg = env->NewStringUTF("wangfeng");
    env->CallStaticVoidMethod(clazz, staticMethod, jmsg);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_joyy_nativecpp_MainActivity_getJavaObjectField(JNIEnv *env, jobject thiz,
                                                        jobject student) {
    jclass  clazz = env->GetObjectClass(student);
    if(clazz == NULL) return;

    // 获取Object实例属性
    jfieldID nameId = env->GetFieldID(clazz, "name", "Ljava/lang/String;");
    jstring  jname = (jstring)env->GetObjectField(student, nameId);

    jfieldID  ageId = env->GetFieldID(clazz, "age", "I");
    jint jage = env->GetIntField(student, ageId);

    const char * name = env->GetStringUTFChars(jname, 0);
    env->ReleaseStringUTFChars(jname, name);

    // 获取java类属性
    jfieldID  gradId = env->GetStaticFieldID(clazz, "grade", "I");
    jint jgrade = env->GetStaticIntField(clazz, gradId);

    jfieldID nickeNameId = env->GetStaticFieldID(clazz, "nickname", "Ljava/lang/String;");
    jstring  jnickname = (jstring)env->GetStaticObjectField(clazz, nickeNameId);

    const char * nickeName = env->GetStringUTFChars(jnickname, 0);
    env->ReleaseStringUTFChars(jnickname, nickeName);

    XLOGI("feifei: name:%s, age:%d, grade:%d, nickname:%s", name, jage, jgrade, nickeName);

    // JNI设置java对象属性
    env->SetObjectField(student, nameId, env->NewStringUTF("张三"));
    // JNI设置java类属性
    env->SetStaticObjectField(clazz, nickeNameId, env->NewStringUTF("小白"));
    jstring jnameNew = (jstring)env->GetObjectField(student, nameId);
    jstring jnickNameNew = (jstring)env->GetStaticObjectField(clazz, nickeNameId);

    const char * newName = env->GetStringUTFChars(jnameNew, 0);
    const char * newNickName = env->GetStringUTFChars(jnickNameNew, 0);

    env->ReleaseStringUTFChars(jnameNew, newName);
    env->ReleaseStringUTFChars(jnickNameNew, newNickName);
    XLOGI("feifei after update name:%s, age:%d, grade:%d, nickName:%s ", newName, jage, jgrade, newNickName);
}

4、JNI对象的全局引用和局部引用

  • 1、强全局引用
    NewGlobalRef用来创建强全局引用的JNI对象
    DeleteGlobalRef用来删除强全局引用的JNI对象
  • 2、弱全局引用
    NewWeakGlobalRef用来创建弱全局引用的JNI对象
    DeleteWeakGlobalRef用来删除弱全局引用的JNI对象
    IsSameObject用来判断两个JNI对象是否相同
//4、JNI对象的全局引用和局部引用
    /**
     * 测试JNI强全局引用 和 弱全局引用
     */
    public native void testJNIReference(Object object);



/**
 * (1) 在JNI接口函数中引用JNIN对象的局部变量,都是对JNI对象的局部引用,所有这些JNI对象都会被自动释放。不过我们也可以采用JNI代码提供的DeleteLocalRef函数来删除一个局部JNI对象引用
 * (2) 对于JNI对象,绝对不能简单的声明一个全局变量,在JNI接口函数里面给这个全局变量赋值这么简单,一定要使用JNI代码提供的管理JNI对象的函数
 * JNI全局引用分为两种:一种全局引用,这种引用会阻止Java垃圾回收器回收JNI代码引用的对象;
 * 另一种是弱全局引用,这种全局引用不会阻止垃圾回收器回收JNI 代码引用的Java对象
 * -NewGlobalRef用来创建强全局引用的JNI对象
 * -DeleteGlobalRef来删除强全局引用的JNI对象
 * -NewWeakGlobalRef用来创建弱去啊句引用的JNI对象
 * -DeleteWeakGlobalRef用来删除弱全局引用的JNI对象
 * -IsSameObject用来判断两个JNI对象是否相同
 */
 jobject gThiz;// 全局JNI对象引用
 jobject gWeakThiz; //全局JNI对象弱引用
extern "C"
JNIEXPORT void JNICALL
Java_com_joyy_nativecpp_MainActivity_testJNIReference(JNIEnv *env, jobject thiz, jobject object) {
    // 声明局部变量clazz
    jclass  clazz = env->GetObjectClass(thiz);

    // 手动释放 局部变量 clazz; DeleteLocalRef 也可不用手动调用,JNI方法返回之后,会自动释放局部JNI变量
    env->DeleteLocalRef(clazz);

    // ----强全局变量
    gThiz = env->NewGlobalRef(object);//生成全局的JNI 对象引用,这样生成的全局的JNI对象 才可以在其他函数中使用

    env->DeleteGlobalRef(gThiz);// 在我们不需要gThis这个全局JNI对象引用时,可以将其删除

    // -----全局弱引用
    gWeakThiz = env->NewWeakGlobalRef(object);//生成全局的JNI对象引用, 这样生成的全局的JNI对象才可以在其它函数中使用

    if(env->IsSameObject(gWeakThiz, nullptr)) {
        XLOGI("全局引用 已经被释放了");
    } else {
        XLOGI("全局引用 还没有被释放");
    }
    // 释放 全局弱引用对象
    env->DeleteWeakGlobalRef(gWeakThiz);
}

JNI进程间同步

/**
     * JNI利用java对象对象进行线程同步
     * @param lock
     */
    public native void testJNILock(Object lock);

extern "C"
JNIEXPORT void JNICALL
Java_com_joyy_nativecpp_MainActivity_testJNILock(JNIEnv *env, jobject thiz, jobject lock) {
    // 加锁
    env->MonitorEnter(lock);
    // doSomething
    XLOGI("feifei, this is in lock");
    // 释放锁
    env->MonitorExit(lock);
}

6. JNI异常相关的函数

/**
     * 1. env->ExceptionOccurred() 判断JNI调用Java方法是否遇到了Exception
     * 2. env->ThrowNew()
     */
    public native void testJavaException();
    public void helloException(){
        Log.d("XPlay", "\n\n");
        Log.d("XPlay", "helloException");
    }


extern "C"
JNIEXPORT void JNICALL
Java_com_joyy_nativecpp_MainActivity_testJavaException(JNIEnv *env, jobject thiz) {
    jclass  clazz = env->GetObjectClass(thiz);
    if(clazz == NULL) return;
    jmethodID  helloException_method = env->GetMethodID(clazz, "helloException", "()V");
    if(helloException_method == NULL) return;
    env->CallVoidMethod(thiz, helloException_method);
    if(env->ExceptionOccurred() != NULL) {
         //env->ExceptionDescribe();
         env->ExceptionClear();
         XLOGI("feifei, 调用java方法时 遇到了Excpetion");
         return;
    } else {
         XLOGI("feifei, 调用java方法时 没有遇到Excpetion");
    }
    XLOGI("feifei, 调用helloException 方法成功了!");

    XLOGI("feifei, now JNI throw java exception - beging");
    jclass  expectionClazz = env->FindClass("java/lang/Exception");
    if(expectionClazz == NULL) return;
    env->ThrowNew(expectionClazz, "this is a exception");
}