• jni-02、lib路径、数组、对象、引用、extern修饰函数


    lib路径

    static {
        // System.load(D:/xxx/xxxx/xxx/native-lib); 这种是可以绝对路径的加载动态链接库文件
        System.loadLibrary("native-lib"); // 这种是从库目录遍历层级目录,去自动的寻找   apk里面的lib/libnative-lib.so
    }
    
    • 1
    • 2
    • 3
    • 4

    Student

    import android.util.Log;
    
    public class Student {
    
        private final static String TAG = Student.class.getSimpleName();
    
        public String name;
        public int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            Log.d(TAG, "Java setName name:" + name);
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            Log.d(TAG, "Java setAge age:" + age);
            this.age = age;
        }
    
        public static void showInfo(String info) {
            Log.d(TAG, "showInfo info:" + info);
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    基本类型、对象类型的数组

    jni中修改Java数组的值

    jint本质是int,所以可以用int接收

    public native void testArrayAction(int count, String textInfo, int[] ints, String[] strs); // String引用类型,玩数组
    
    • 1
    public void test01(View view) {
        int[] ints = new int[]{1,2,3,4,5,6}; // 基本类型的数组
    
        String[] strs = new String[]{"李小龙","李连杰","李元霸"}; // 对象类型的数组
    
        testArrayAction(99, "你好", ints, strs);
    
        for (int anInt : ints) {
            Log.d(TAG, "Java test01: anInt:" + anInt);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    // jint == int
    // jstring == String
    // jintArray == int[]
    // jobjectArray == 引用类型对象,例如 String[]   Test[]   Student[]  Person[]
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_derry_as_1jni_1project_MainActivity_testArrayAction(JNIEnv *env, jobject thiz,
                                                                 jint count,
                                                                 jstring text_info,
                                                                 jintArray ints,
                                                                 jobjectArray strs) {
        // ① 基本数据类型  jint count 最简单的
        int countInt = count; // jint本质是int,所以可以用int接收
        LOGI("参数一 countInt:%d\n", countInt);
    
        // const char* GetStringUTFChars(jstring string, jboolean* isCopy)
        const char *textInfo = env->GetStringUTFChars(text_info, NULL);
        LOGI("参数二 textInfo:%s\n", textInfo);
    
        // ② 把int[] 转成 int*
        // jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
        int *jintArray = env->GetIntArrayElements(ints, NULL);
    
        // Java层数组的长度
        // jsize GetArrayLength(jarray array) // jintArray ints 可以放入到 jarray的参数中去
        jsize size = env->GetArrayLength(ints);
    
        for (int i = 0; i < size; ++i) {
            *(jintArray + i) += 100; // C++的修改,影响不了Java层
            LOGI("参数三 int[]:%d\n", *jintArray + i);
        }
        // 目前无法控制Java的数组 变化 +100
        // 操作杆 ----> JMV
        // env->
    
        /**
         * ReleaseIntArrayElements第三个参数mode:
         * 0:           刷新Java数组,并 释放C++层数组
         * JNI_COMMIT:  只提交 只刷新Java数组,不释放C++层数组
         * JNI_ABORT:   只释放C++层数组
         */
        env->ReleaseIntArrayElements(ints, jintArray, 0);
    
        // ③:jobjectArray 代表是Java的引用类型数组,不一样
        jsize strssize = env->GetArrayLength(strs);
        for (int i = 0; i < strssize; ++i) {
    
            jstring jobj = static_cast(env->GetObjectArrayElement(strs, i));
    
            // 模糊:isCopy内部启动的机制
            // const char* GetStringUTFChars(jstring string, jboolean* isCopy)
            const char *jobjCharp = env->GetStringUTFChars(jobj, NULL);
    
            LOGI("参数四 引用类型String 具体的:%s\n", jobjCharp);
    
            // 释放jstring
            env->ReleaseStringUTFChars(jobj, jobjCharp);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    对象

    jni获取jclass的两种方式

    public native void putObject(Student student, String str); // 传递引用类型,传递对象
    
    • 1
    public void test02(View view) {
        Student student = new Student(); // Java new
        student.name = "史泰龙";
        student.age = 88;
        putObject(student, "九阳神功");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // jobject student == Student
    // jstring str  == String
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_derry_as_1jni_1project_MainActivity_putObject(JNIEnv *env,
                                                           jobject thiz,
                                                           jobject student,
                                                           jstring str) {
        const char *strChar = env->GetStringUTFChars(str, NULL);
        LOGI("strChar:%s\n", strChar);
        env->ReleaseStringUTFChars(str, strChar);
    
        // --------------
        // 1.寻找类 Student
        // jclass studentClass = env->FindClass("com/derry/as_jni_project/Student"); // 第一种
        jclass studentClass = env->GetObjectClass(student); // 第二种
    
        // 2.Student类里面的函数规则  签名
        jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");
        jmethodID getName = env->GetMethodID(studentClass, "getName", "()Ljava/lang/String;");
        jmethodID showInfo = env->GetStaticMethodID(studentClass, "showInfo", "(Ljava/lang/String;)V");
    
        // 3.调用 setName
        jstring value = env->NewStringUTF("AAAA");
        env->CallVoidMethod(student, setName, value);
    
        // 4.调用 getName
        jstring getNameResult = static_cast(env->CallObjectMethod(student, getName));
        const char *getNameValue = env->GetStringUTFChars(getNameResult, NULL);
        LOGE("调用到getName方法,值是:%s\n", getNameValue);
    
        // 5.调用静态showInfo
        jstring jstringValue = env->NewStringUTF("静态方法你好,我是C++");
        env->CallStaticVoidMethod(studentClass, showInfo, jstringValue);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    Person
    import android.util.Log;
    
    public class Person {
    
        private static final String TAG = Person.class.getSimpleName();
    
        public Student student;
    
        public void setStudent(Student student) {
            Log.d(TAG, "call setStudent student:" + student.toString());
            this.student = student;
        }
    
        public static void putStudent(Student student) {
            Log.d(TAG, "call static putStudent student:" + student.toString());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    创建Java对象

    jobject jclass jstring … 【函数结束后,会自动释放】

    还是要养成用完即释放的习惯

    jni函数没有堆栈概念,只有局部引用、全局引用

    public native void insertObject(); // 凭空创建Java对象
    
    • 1
    // C++ 堆 栈 ...
    // JNI函数  局部引用,全局引用,...
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_derry_as_1jni_1project_MainActivity_insertObject(JNIEnv *env, jobject thiz) {
        /*jstring str = env->GetStringUTFChars();
        jstring str = env->GetStringUTFChars();
        jstring str = env->GetStringUTFChars();
        jstring str = env->GetStringUTFChars();
        jstring str = env->GetStringUTFChars();
        jstring str = env->GetStringUTFChars();
        jstring str = env->GetStringUTFChars();
    
        // 好习惯:
        // 我用完了,我记释放,在我函数执行过程中,不会导致 内存占用多
        env->ReleaseStringUTFChars()*/
    
        // 1.通过包名+类名的方式 拿到 Student class  凭空拿class
        const char *studentstr = "com/derry/as_jni_project/Student";
        jclass studentClass = env->FindClass(studentstr);
    
        // 2.通过student的class  实例化此Student对象
        jobject studentObj = env->AllocObject(studentClass); // AllocObject 只实例化对象,不会调用对象的构造函数
    
        // 方法签名的规则
        jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");
        jmethodID setAge = env->GetMethodID(studentClass, "setAge", "(I)V");
    
        // 调用方法
        jstring strValue = env->NewStringUTF("Derry");
        env->CallVoidMethod(studentObj, setName, strValue);
        env->CallVoidMethod(studentObj, setAge, 99);
    
        // env->NewObject() // NewObject 实例化对象,会调用对象的构造函数
    
        // ====================  下面是 Person对象  调用person对象的  setStudent 函数等
    
        // 4.通过包名+类名的方式 拿到 Student class  凭空拿class
        const char *personstr = "com/derry/as_jni_project/Person";
        jclass personClass = env->FindClass(personstr);
    
        jobject personObj = env->AllocObject(personClass); // AllocObject 只实例化对象,不会调用对象的构造函数
    
        // setStudent 此函数的 签名 规则
        jmethodID setStudent = env->GetMethodID(personClass, "setStudent",
                "(Lcom/derry/as_jni_project/Student;)V");
    
        env->CallVoidMethod(personObj, setStudent, studentObj);
    
        // 规范:一定记得释放【好习惯】
        // 第一类
        env->DeleteLocalRef(studentClass);
        env->DeleteLocalRef(personClass);
        env->DeleteLocalRef(studentObj);
        env->DeleteLocalRef(personObj);
    
        // 第二类
        // env->ReleaseStringUTFChars()
    
        // TODO 局部引用: jobject jclass jstring ...  【函数结束后,会自动释放】
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    Dog

    import android.util.Log;
    
    public class Dog { // NewObject 调用我们的构造函数
    
        public Dog() { // 
            Log.d("Dog", "Dog init...");
        }
    
        public Dog(int n1) { // 
            Log.d("Dog", "Dog init... n1:" + n1);
        }
    
        public Dog(int n1, int n2) { // 
            Log.d("Dog", "Dog init... n1:" + n1 + " n2:" + n2);
        }
    
        public Dog(int n1, int n2, int n3) { // 
            Log.d("Dog", "Dog init... n1:" + n1 + " n2:" + n2 + " n3:" + n3);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    引用

    public native void testQuote(); // 测试引用
    
    • 1
    jclass dogClass; // 你以为这个是全局引用,实际上他还是局部引用
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_derry_as_1jni_1project_MainActivity_testQuote(JNIEnv *env, jobject thiz) {
        if (NULL == dogClass) {
            /*const char * dogStr = "com/derry/as_jni_project/Dog";
            dogClass = env->FindClass(dogStr);*/
    
            // 升级全局引用: JNI函数结束也不释放,反正就是不释放,必须手动释放   ----- 相当于: C++ 对象 new、手动delete
            const char * dogStr = "com/derry/as_jni_project/Dog";
            jclass temp = env->FindClass(dogStr);
            dogClass = static_cast(env->NewGlobalRef(temp)); // 提升全局引用
            // 记住:用完了,如果不用了,马上释放,C C++ 工程师的赞美
            env->DeleteLocalRef(temp);
        }
    
        //  V  是不会变的
    
        // 构造函数一
        jmethodID init = env->GetMethodID(dogClass, "", "()V");
        jobject dog = env->NewObject(dogClass, init);
    
        // 构造函数2
        init = env->GetMethodID(dogClass, "", "(I)V");
        dog = env->NewObject(dogClass, init, 100);
    
    
        // 构造函数3
        init = env->GetMethodID(dogClass, "", "(II)V");
        dog = env->NewObject(dogClass, init, 200, 300);
    
        // 构造函数4
        init = env->GetMethodID(dogClass, "", "(III)V");
        dog = env->NewObject(dogClass, init, 400, 500, 600);
    
        env->DeleteLocalRef(dog); // 释放
        // dogClass = NULL; // 不能这样干(JNI函数结束后,还怎么给你释放呢)
    
        // 这样就解决了
        /*env->DeleteGlobalRef(dogClass);
        dogClass = NULL;*/
    }
    
    // JNI函数结束,会释放局部引用 假如dogClass没有设置为引用  dogClass虽然被释放,但是还不等于NULL,只是一个悬空指针而已,所以第二次进不来IF,会奔溃
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    释放全局引用

    public native void delQuote(); // 释放全局引用
    
    • 1
    // 手动释放全局引用
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_derry_as_1jni_1project_MainActivity_delQuote(JNIEnv *env, jobject thiz) {
       if (dogClass != NULL) {
           LOGE("全局引用释放完毕,上面的按钮已经失去全局引用,再次点击会报错");
           env->DeleteGlobalRef(dogClass);
           dogClass = NULL; // 最好给一个NULL,指向NULL的地址,不要去成为悬空指针,为了好判断悬空指针的出现
       }
    
       // 测试下
       show();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    extern修饰函数 可以直接在其他cpp文件中实现

    • native-lib.cpp
    extern int age; // 声明age
    extern void show(); // 声明show函数  5000行代码
    
    • 1
    • 2
    • Test.cpp
    #include 
    
    // 日志输出
    #include 
    
    #define TAG "JNISTUDY"
    // __VA_ARGS__ 代表 ...的可变参数
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG,  __VA_ARGS__);
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG,  __VA_ARGS__);
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG,  __VA_ARGS__);
    
    int age = 99; // 实现
    
    void show() { // 实现
        // 5000行代码
        // ...
        LOGI("show run age:%d\n", age);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    hadoop主要组建简写笔记
    DockerCompose部署系列:构建ELK并同步MySQL数据
    MVC您正在查找的资源可能被移除
    恐龙做梦都想要的能力:人类首次小行星撞击实验
    业务可视化-让你的流程图"Run"起来(2.问题与改进)
    linux、windows 查看java等进程占用资源情况
    Tomcat部署及优化
    week9|查阅文章 Mac os M2芯片 arm64架构|安装paddlepaddle问题
    密码学-密码协议之零知识证明
    一文看懂推荐系统:排序01:多目标模型
  • 原文地址:https://blog.csdn.net/weixin_37165769/article/details/126165770