美文网首页
NDK-015: jni: 缓存策略和异常处理

NDK-015: jni: 缓存策略和异常处理

作者: xqiiitan | 来源:发表于2025-01-03 16:22 被阅读0次

JNI04- 缓存策略和异常处理

1.数组的细节处理

void qsort(void * _Base, size_t _NumOfElements, size_t _SizeOfElements, 
    int(* _PtFuncCompare)(const void *, const void *))
    // 第一个参数:void* 数组的首地址
    // 第二个参数:数组的大小长度
    // 第三个参数:数组元素数据类型的大小
    // 第四个参数:数组的一个比较方法指针(Comparable)
    qsort(intArray, length, sizeof(int), compare);

2.局部引用和全局引用

全局的引用:视频转码压缩,通过回调方法compress_callback, 不断的给Java层回传进度。
static jobject callback_jobj = NULL; // 全局的保存对象。
static JNIEnv *mEnv;
最后,需要释放
env->DeleteGlobalRef(callback_jobj);

3.静态缓存策略

详见代码部分

4.jni的异常处理

native 出错了,没办法在 java 层去 try,处理的方式一般两种:
第一种是补救,name3拿不到就拿别的name;
第二种就是构建 java 层的异常对象往上抛。

能看懂的代码:增量更新(ok), 签名校验 ,在 native 层写一个文件的切割库(实现大文件切割、合并),
文件的加密 (.so)

5.相关代码

5.1 Eclipse代码

package com.darren.day15;

public class Simple1 {
    private static String name; // 一大堆变量
    private static String name1;// 一大堆变量
    private static String name3;// 一大堆变量

    static {
//      System.load("C:/Users/hcDarren/Desktop/android/NDK/NDK_Day15_VS/x64/Debug/NDK_Day15_VS.dll");
        System.load("D:/Java/NDK_Day15_VS.dll");
    }
    public Simple1() { // 构造方法
    }

    public static void main(String[] args) {
        // 1. 数组处理的一些细节
        int[] arr = { 11, 22, -3, 2, 4, 6, -15 };
        sort(arr); // 排序,在jni实现排序
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        } // 并没有进行排序,要处理ReleaseIntArrayElements 的第四个参数-模型。
        System.out.println();

        // 2. jni的局部引用和全局引用
        localRef(); // jni 局部引用
        saveGlobalRef("Darren"); // 保存全局变量,其他方法需要用到
        // 拿到jni的全局变量,并打印
        System.out.println("getGlobalRef--> " + getGlobalRef());
        // 合适的时机去释放
        delteGlobalRef();
        // 再次获取,会打印空指针java.lang.NullPointerException
        // System.out.println(getGlobalRef());
            
        // 3.缓存策略 static native层有一大堆方法要去获取name属性
        // 初始化全局静态缓存
        initStaticCache();
        staticLocalCache("Darren");
        System.out.println("1 name = " + name);
        staticLocalCache("Jack");// jni打印:not null
        System.out.println("2 name = " + name); // name被修改了。
        staticLocalCache("Rose"); // jni打印:not null 
        System.out.println("3 name = " +name);
        // 先打印java层日志,再打印C层的日志。

        // 4.C层的异常处理 (简单讲,C++还会讲解)
        try {
            exception(); // 捕获jni层抛出的异常。
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            // java.lang.NoSuchFieldError: name3
        }
        System.out.println("name3 = " + name3);
    }

    private static final native void exception() throws NoSuchFieldException;

    private static final native void initStaticCache();

    private static final native void staticLocalCache(String name);

    private static final native void delteGlobalRef();

    private static final native String getGlobalRef();

    private static final native void saveGlobalRef(String str);

    private static final native void localRef();
    // 在jni层实现排序算法
    private static final native void sort(int[] arr);
}

Simple1.java 生成的.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_darren_day15_Simple1 */

#ifndef _Included_com_darren_day15_Simple1
#define _Included_com_darren_day15_Simple1
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_darren_day15_Simple1
 * Method:    sort
 * Signature: ([I)V
 */
JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_sort
  (JNIEnv *, jclass, jintArray);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_localRef
(JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_saveGlobalRef
(JNIEnv *, jclass,jstring);

JNIEXPORT jstring JNICALL Java_com_darren_day15_Simple1_getGlobalRef
(JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_delteGlobalRef
(JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_staticLocalCache
(JNIEnv *, jclass, jstring);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_initStaticCache
(JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_exception
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

5.2 VisualStudio代码

// Simple.c

#include "com_darren_day15_Simple1.h"
#include <stdlib.h>

// 比较的方法:对里面的值进行比较
int compare(const jint *number1, const jint *number2){
    return *number1 - *number2;
}
// 全局变量,其他方法需要用到
jstring globalStr;

// int数组排序
JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_sort
(JNIEnv * env, jclass jclz, jintArray jarray){
    // 对 jarray 进行排序 (java系统函数 sort)
    jint* intArray = (*env)->GetIntArrayElements(env,jarray,NULL);
    int length = (*env)->GetArrayLength(env,jarray); // 数组大小

    // 第一个参数:void* 数组的首地址
    // 第二个参数:数组的大小长度
    // 第三个参数:数组元素数据类型的大小
    // 第四个参数:数组的一个比较方法(函数)的指针(Comparable)
    qsort(intArray, length, sizeof(int), compare);

    // 同步数组的数据给 java 数组,intArray 并不是 jarray ,可以简单的理解为 copy
    // 0 : 既要同步数据给 jarray ,又要释放 intArray
    // JNI_COMMIT:  会同步数据给 jarray ,但是不会释放 intArray
    // JNI_ABORT: 不同步数据给 jarray ,但是会释放 intArray
    (*env)->ReleaseIntArrayElements(env,jarray,intArray, 0);
}

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_localRef
(JNIEnv *env, jclass jclz){
    // 在 native 层构建的 Java 对象,你不用了该怎么管理?都需要自己释放内存。
    // native 层开辟的内存由谁管理,你能开辟多大? 

    // 字符串截取,构建 String 对象
    jclass str_clz = (*env)->FindClass(env,"java/lang/String");
    jmethodID init_mid = (*env)->GetMethodID(env,str_clz,"<init>","()V");
    jobject j_str = (*env)->NewObject(env, str_clz, init_mid);

    // 还有 100 行代码......

    // jobject 不要再使用了,要回收j_str。 回头看 javaGC 的源码
    (*env)->DeleteLocalRef(env, j_str);
    // 删除了就不能再使用了,C 和 C++ 都需要自己释放内存(静态开辟的不需要,动态开辟的都需要回收)
}

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_saveGlobalRef
(JNIEnv *env, jclass jclz, jstring str){
    // 创建全局引用:保存全局变量,其他方法需要用到
    globalStr = (*env)->NewGlobalRef(env, str);
    // NewWeakGlobalRef (java 中的软引用很像) 无法保证对象不为空,可能会被清理掉了。
}
// 获取全局引用
JNIEXPORT jstring JNICALL Java_com_darren_day15_Simple1_getGlobalRef
(JNIEnv *env, jclass jclz){
    return globalStr;
}
// 删除全局引用
JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_delteGlobalRef
(JNIEnv *env, jclass jclz){
    (*env)->DeleteGlobalRef(env,globalStr);
}

//JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_staticLocalCache
//(JNIEnv *env, jclass jclz, jstring name){
//  // name属性 赋值操作
//  static jfieldID f_id = NULL;// static局部缓存,这个方法会被多次调用,不需要反复的去获取jfieldID  OpenCV WebRtc视频通话
//  if (f_id == NULL){
//      f_id = (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
//  }else{
//      printf("fieldID is not null\n");
//  }
//  (*env)->SetStaticObjectField(env, jclz, f_id, name);
//}

// 全局静态缓存,一般在构造函数中初始化的时候会去缓存
static jfieldID f_name_id = NULL;
static jfieldID f_name1_id = NULL;
static jfieldID f_name2_id = NULL;

// 设置静态缓存
JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_staticLocalCache
(JNIEnv *env, jclass jclz, jstring name){
    // 如果这个方法会反复的被调用,那么不会反复的去获取 jfieldID,在initStaticCache方法中已经获取好了。
    (*env)->SetStaticObjectField(env, jclz, f_name_id, name);
    // (*env)->SetStaticObjectField(env, jclz, f_name1_id, name); // 给name1也赋值。
}

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_initStaticCache
(JNIEnv *env, jclass jclz){
    // 初始化全局静态缓存
    f_name_id = (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
    f_name1_id = (*env)->GetStaticFieldID(env, jclz, "name1", "Ljava/lang/String;");
    f_name2_id = (*env)->GetStaticFieldID(env, jclz, "name3", "Ljava/lang/String;");
}

// 4.C层处理异常
JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_exception
(JNIEnv *env, jclass jclz){
    
    // 假设现在想给 ,name 赋值 name3
    jfieldID f_id = (*env)->GetStaticFieldID(env, jclz, "name3", "Ljava/lang/String;");

    // 好几种方式:
    // 1. 补救措施 ,name3找不到 我就去拿 name
    // 1.1 检测有没有异常
    jthrowable throwable = (*env)->ExceptionOccurred(env);
    /*if (throwable){ // 有异常
        // 补救措施,先把异常清除
        printf("有异常");
        // 清除现有的异常
        (*env)->ExceptionClear(env);
        // 重新获取 name 属性
        f_id = (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
    }*/

    // 2. 不做补救措施:想给 java 层抛一个异常
    if (throwable){
        // 清除异常
        (*env)->ExceptionClear(env);
        // Throw 抛一个 java 的 Throwable 对象
        jclass no_such_clz = (*env)->FindClass(env,"java/lang/NoSuchFieldException");
        (*env)->ThrowNew(env, no_such_clz, "NoSuchFieldException name3");

        return;// 记得 return,不能少,否则catch不到 e。
    }

    jstring name = (*env)->NewStringUTF(env, "Darren 666");
    (*env)->SetStaticObjectField(env, jclz, f_id, name); // 给java层name3 赋值。
}

过一遍所有视频。
在公司再也不怕什么东西。

相关文章

网友评论

      本文标题:NDK-015: jni: 缓存策略和异常处理

      本文链接:https://www.haomeiwen.com/subject/cowfyjtx.html