jni-05、排序、静态和非静态缓存、异常、模拟JNIEnv
2022-09-05 本文已影响0人
喂_balabala
排序
Java
// public native void sort(int[] arr);
external fun sort(arr: IntArray)
// static { System.loadLibrary("native-lib"); }
companion object {
init {
System.loadLibrary("native-lib")
}
}
// 点击事件
fun sortAction(view: View) {
val arr = intArrayOf(11, 22, -3, 2, 4, 6, -15)
sort(arr)
for (element in arr) {
Log.e("eeeee", "sortAction: " + element.toString() + "\t")
}
}
native
// 比较函数了
int compare(const jint * number1, const jint * number2) {
return *number1 - *number2;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity_sort(JNIEnv *env, jobject thiz, jintArray arr) {
// 对 arr排序 内置的函数
jint * intArray = env->GetIntArrayElements(arr, nullptr);
int length = env->GetArrayLength(arr);
// NDK 很大的工具链(Java JNI,C++,stdlib ....) 工具箱
/**
* 参数1:void * 数组的首地址
* 参数2:数组的大小长度
* 参数3:元素的大小
* 参数4:对比的方法指针
*/
qsort(intArray, length, sizeof(int),
reinterpret_cast<int (*)(const void *, const void *)>(compare));
env->ReleaseIntArrayElements(arr, intArray, 0); // 0 操纵杆 更新KT的数组
}
非静态缓存(普通缓存)
- Java
static {
System.loadLibrary("native-lib");
}
// 假设这里定义了一堆变量
static String name1 ="T1";
static String name2 ="T2";
static String name3 ="T3";
static String name4 ="T4";
static String name5 ="T5";
static String name6 ="T6";
public static native void localCache(String name); // 普通的局部缓存,弊端演示
//点击事件
public void staticCacheAction(View view) {
localCache("AAAA");
}
- native
extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity2_localCache(JNIEnv *env, jclass clazz, jstring name) {
// 非静态缓存
jfieldID f_id = nullptr;
if (f_id == nullptr) {
f_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;"); // 有点耗费性能
} else {
LOGE("空的");
}
env->SetStaticObjectField(clazz, f_id, name); // 修改 AAA
f_id = nullptr;
}
静态缓存
- Java
// 假设这里定义了一堆变量
static String name1 ="T1";
static String name2 ="T2";
static String name3 ="T3";
static String name4 ="T4";
static String name5 ="T5";
static String name6 ="T6";
public static native void initStaticCache(); // 初始化静态缓存
public static native void staticCache(String name);
public static native void clearStaticCache(); // 清除化静态缓存
// 点击事件
public void staticCacheAction(View view) {
// 初始化静态缓存
initStaticCache(); // 如果是在类里面, Person Student ,必须在类的构造函数初始化
staticCache("BBB");
staticCache("BBB");
staticCache("BBB");
staticCache("BBB");
staticCache("BBB");
staticCache("BBB");
staticCache("BBB");
}
@Override
protected void onDestroy() {
super.onDestroy();
clearStaticCache(); // 清除化静态缓存
}
- native
// 像OpenCV,WebRTC,等等 大量使用 静态缓存 ..
static jfieldID f_name1_id = nullptr;
static jfieldID f_name2_id = nullptr;
static jfieldID f_name3_id = nullptr;
static jfieldID f_name4_id = nullptr;
static jfieldID f_name5_id = nullptr;
static jfieldID f_name6_id = nullptr;
// 先缓存
extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity2_initStaticCache(JNIEnv *env, jclass clazz) {
f_name1_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;");
f_name2_id = env->GetStaticFieldID(clazz, "name2", "Ljava/lang/String;");
f_name3_id = env->GetStaticFieldID(clazz, "name3", "Ljava/lang/String;");
f_name4_id = env->GetStaticFieldID(clazz, "name4", "Ljava/lang/String;");
f_name5_id = env->GetStaticFieldID(clazz, "name5", "Ljava/lang/String;");
f_name6_id = env->GetStaticFieldID(clazz, "name6", "Ljava/lang/String;");
}
// 使用
extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity2_staticCache(JNIEnv *env, jclass clazz, jstring name) {
// 不会反复 GetStaticFieldID 提供性能
env->SetStaticObjectField(clazz, f_name1_id, name);
env->SetStaticObjectField(clazz, f_name2_id, name);
env->SetStaticObjectField(clazz, f_name3_id, name);
env->SetStaticObjectField(clazz, f_name4_id, name);
env->SetStaticObjectField(clazz, f_name5_id, name);
env->SetStaticObjectField(clazz, f_name6_id, name);
}
// 清除
extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity2_clearStaticCache(JNIEnv *env, jclass clazz) {
f_name1_id = nullptr;
f_name2_id = nullptr;
f_name3_id = nullptr;
f_name4_id = nullptr;
f_name5_id = nullptr;
f_name6_id = nullptr;
}
异常处理
- Java
public class MainActivity3 extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
static String name1 = "T1";
// 下面是异常处理
public static native void exception();
public static native void exception2() throws NoSuchFieldException; // NoSuchFieldException接收C++层抛上来的异常
public static native void exception3();
public static native String derryAction();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void exceptionAction(View view) {
exception(); // C++层自己做了补救措施了
// 捕获人家C++层抛上来的异常
try {
exception2();
} catch (NoSuchFieldException exception) {
exception.printStackTrace();
Log.d("Derry", "exceptionAction: 异常被我捕获了");
}
exception3();
String result = derryAction();
}
// 专门给 C++(native层) 层调用的 函数
public static void show() throws Exception {
Log.d("eeee", "show: 1111");
Log.d("eeee", "show: 1111");
Log.d("eeee", "show: 1111");
Log.d("eeee", "show: 1111");
Log.d("eeee", "show: 1111");
Log.d("eeee", "show: 1111");
throw new NullPointerException("我是Java中抛出的异常,我的show方法里面发送了Java逻辑错误");
}
}
- native
// 异常1 【native层主动干的异常】
extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity3_exception(JNIEnv *env, jclass clazz) {
// 假设现在想操作 name999 ,没有name999就会在native层奔溃掉
jfieldID f_id = env->GetStaticFieldID(clazz, "name999", "Ljava/lang/String;");
// 奔溃后,有两种解决方案
// 方式1 补救措施
jthrowable thr = env->ExceptionOccurred(); // 监测本次执行,到底有没有异常 JNI函数里面代码有问题
if(thr) { // 非0 进去,监测到有异常
LOGD("C++层有异常 监测到了");
env->ExceptionClear(); // 此异常被清除
// 开始 补救措施
jfieldID f_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;");
}
}
// 异常2 【native层主动干的异常】
extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity3_exception2(JNIEnv *env, jclass clazz) {
// 假设现在想操作 name999 ,没有name999就会在native层奔溃掉
jfieldID f_id = env->GetStaticFieldID(clazz, "name8888", "Ljava/lang/String;");
// 奔溃后,有两种解决方案
// 方式2 往Java层抛
jthrowable jthrowable = env->ExceptionOccurred(); // 监测本次执行,到底有没有异常 JNI函数里面代码有问题
if(jthrowable) { // 非0 进去,监测到有异常
LOGD("C++层有异常 监测到了");
env->ExceptionClear(); // 此异常被清除
// Throw抛一个 Java的对象 java/lang/String java/xxxxx/xxx/NullExxx
jclass clz = env->FindClass("java/lang/NoSuchFieldException");
env->ThrowNew(clz, "NoSuchFieldException 是在是找不到 name8888啊,没有办法,抛给你了");
}
}
// 异常3 【native层被动干的异常 被动 我是Java方法坑了】
extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity3_exception3(JNIEnv *env, jclass clazz) {
jmethodID showID = env->GetStaticMethodID(clazz, "show", "()V");
env->CallStaticVoidMethod(clazz, showID); // 是不是这句话奔溃的 1是 2不是 答:不是,只是他引起的而已
// ExceptionCheck 《==》 慢慢的奔溃的,相当于给了你空余时间,既然不是马上奔溃,我就可以检测
// JNI函数里面代码有问题 没有问题,给你空余时间,慢慢的奔溃的
if (env->ExceptionCheck()) {
env->ExceptionDescribe(); // 输出描述 信息
env->ExceptionClear(); // 此异常被清除 业务逻辑控制
}
// 注意实现:
/*// 奔溃后,下面的语句,照样打印
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>1");
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>2");
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>3");
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>4");
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>5");
env->NewStringUTF("AAAA"); // 局部引用 奔溃给磨平*/
}
C++异常
#include <iostream>
#include <string>
using namespace std;
void exceptionMethod01() {
throw "我报废了"; // const char *
}
class Student {
public:
char * getInfo() {
return "自定义";
}
};
void exceptionMethod02() {
Student student;
throw student;
}
int main() {
try {
exceptionMethod01();
} catch ( const char * & msg) {
cout << "捕获到异常1:" << msg << endl;
}
try {
exceptionMethod02();
} catch (Student & msg) {
cout << "捕获到异常2:" << msg.getInfo() << endl;
}
return 0;
}
模拟JNIEnv
// JNI 怎么学习?
// 1.JNIEnv 300个函数 常用的函数就行了
// 2.手写JNIEnv
#include <iostream>
#include <string>
using namespace std;
// 模拟 jstring
typedef char * jstring;
// 模拟 jobject
typedef char * jobject;
// 如果是C语言 C++也是会调用C的 JNINativeInterface
typedef const struct JNINativeInterface * JNIEnv; // 定义了一个结构体指针的别名 一级指针
struct JNINativeInterface {
// 300多个 函数指针 声明 ------- 指针函数对应的实现 我们现在看不到 在库
// ...
jstring (*NewStringUTF) (JNIEnv *, char *); // 函数指针 声明
};
// 指针函数对应的实现 我们现在看不到 在库
jstring NewStringUTF(JNIEnv * env, char * str) {
// 注意:在真正的源码中,这里需要很多复杂的代码来转换 (毕竟涉及到跨越语言操作了), 我们就简写了
// char * str <---> jstring str
return str;
}
jstring Java_com_derry_as_1jni_1project_MainActivity3_derryAction(JNIEnv *env, jobject job) {
// env 已经是二级指针了
return (*env)->NewStringUTF(env, "9527"); // 函数指针 不起作用
}
// 模拟 紫色区域 内部处理的逻辑 我们看不到
int main() {
// 构建 JNIEnv *
struct JNINativeInterface nativeInterface;
// 把结构体函数指针 进行赋值 (函数指针---实现)
nativeInterface.NewStringUTF = NewStringUTF;
// 处理生成好的 JNIEnv * 后,传递给 JNI函数
JNIEnv env = &nativeInterface; // 本来就是 一级指针
JNIEnv * jniEnv = &env; // 二级指针
jstring result = Java_com_derry_as_1jni_1project_MainActivity3_derryAction(jniEnv, "com/derry/jni/MainActivity");
// 把jstring 转换 给 Java String 简化了
// jstring -----> String
printf("Java层拿到 C++给我们的 String result:%s", result);
}
总结
- native崩溃后会有一点反应时间可以执行后面的代码,但不能创建局部引用