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的数组
}

非静态缓存(普通缓存)

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");
}
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;
}

静态缓存

// 假设这里定义了一堆变量
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(); // 清除化静态缓存
}
// 像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;
}

异常处理

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逻辑错误");
    }
}

// 异常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);
}

总结

上一篇下一篇

猜你喜欢

热点阅读