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 赋值。
}
过一遍所有视频。
在公司再也不怕什么东西。
网友评论