美文网首页Android 常规武器
JNI-NDK(Java和Native的互相调用)

JNI-NDK(Java和Native的互相调用)

作者: 大虾啊啊啊 | 来源:发表于2022-08-26 15:30 被阅读0次

    1、实现效果

    Screenshot_20220827-120825.png

    2、Java代码

    package com.hvm.vender.jni_01;
    
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    
    import com.hvm.vender.jni_01.databinding.ActivityMainBinding;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    public class MainActivity extends AppCompatActivity {
        public static String TAG = MainActivity.class.getSimpleName() + "_JAVA";
    
        // Used to load the 'jni_01' library on application startup.
        static {
            System.loadLibrary("jni_01");
        }
    
        private ActivityMainBinding binding;
    
        public static final int NUMBER = 100;
        public String name = "daxiaa"; // 签名:Ljava/lang/String;
        public static int age = 30; // 签名:I
        public int count = 800;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            binding = ActivityMainBinding.inflate(getLayoutInflater());
            setContentView(binding.getRoot());
            initListener();
        }
    
        private void initListener() {
            binding.btnUpdate.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d(TAG, "修改之之前的值: " + name);
                    changeName();
                    Log.d(TAG, "修改之后的值: " + name);
    
                }
            });
            binding.btnUpdate2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d(TAG, "修改之之前的值: " + age);
                    changeAge();
                    Log.d(TAG, "修改之后的值: " + age);
                }
            });
            binding.btnCallback.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    callBack();
                }
            });
            binding.btnCallback2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String data = javaToNativeNoParams();
                    Log.d(TAG, "Java调用C++函数拿到的返回值: " + data);
                }
            });
            binding.btnJavaC.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int age = 29;
                    String sex = "boy";
                    int[] array = {1, 2, 3, 7, 8, 7};
                    String[] strs = new String[]{"daxiaa", "pig", "dog"};
                    javaToNativeParams(age, sex, array, strs);
                }
            });
            binding.btnJavaCObj.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Student student = new Student();
                    javaToNativeObj(student, "hello world");
                }
            });
            binding.btnCJavaObj.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                   Student student = nativeNewObj();
                    Log.d(TAG, "C++创建的对象返回值: "+student.toString());
                }
            });
            binding.btnGlobalReference.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    globalReference();
                }
            });
            binding.btnExtern.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    extern();
                }
            });
        }
    
        /**
         * extern关键字测试
         */
        native void extern();
    
        /**
         * 全局引用测试
         */
        native void globalReference();
    
        /**
         * native创建Java对象
         * @return
         */
        native Student nativeNewObj();
    
        /**
         * java ->native传递对象
         * @param student
         * @param hello_world
         */
        native void javaToNativeObj(Student student, String hello_world);
    
        /**
         * java->native传递基本类似、String  数组
         *
         * @param age
         * @param sex
         * @param array
         * @param strs
         */
        native void javaToNativeParams(int age, String sex, int[] array, String[] strs);
    
        /**
         * java->native无参数
         * @return
         */
        native String javaToNativeNoParams();
    
        /**
         * native调用java函数
         * 有返回值
         * @param a
         * @param b
         * @return
         */
        public String nativeToJava2(int a, String b) {
            Log.d(TAG, " C++调用Java中的函数 a = " + a + ",b = " + b);
            return "c++ is pig";
        }
    
        /**
         * native调用java函数
         * 无返回值,有参数
         * @param a
         * @param b
         */
        public void nativeToJava(int a, String b) {
            Log.d(TAG, "nativeToJava: C++调用Java中的函数a = " + a + ",b = " + b);
        }
        /**
         * native调用java函数
         * 无返回值,无参数
         */
        native void callBack();
    
        //native 修改 name、age的值
        native void changeName();
    
        //native 静态的本地方法
        native static void changeAge();
    
    
    
    
    }
    

    3、Native代码

    #include <jni.h>
    #include <string>
    
    // NDK工具链里面的 log 库 引入过来
    #include <android/log.h>
    
    #define TAG "MainActivity_C"
    // ... 我都不知道传入什么  借助JNI里面的宏来自动帮我填充
    #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__)
    
    //// extern "C": 必须采用C的编译方式,为什么,请看JNIEnv内部源码
    //// // 无论是C还是C++ 最终是调用到 C的JNINativeInterface,所以必须采用C的方式 extern "C"
    //// 函数的实现
    //extern "C"
    //
    //JNIEXPORT  // 标记该方法可以被外部调用(VS上不加入 运行会报错, AS上不加入运行没有问题)
    //// Linux运行不加入,不报错,  Win 你必须加入 否则运行报错,   MacOS 还不知道
    //
    //jstring // Java <---> native 转换用的
    //
    //JNICALL // 代表是 JNI标记,可以少
    //
    //// Java_包名_类名_方法名  ,注意:我们的包名 _     native _1
    //
    //// JNIEnv * env  JNI:的桥梁环境    300多个函数,所以的JNI操作,必须靠他
    //
    //// jobject jobj  谁调用,就是谁的实例  MainActivity this
    //// jclass clazz 谁调用,就是谁的class MainActivity.class
    /**
     * 实现在c中修改java中的成员属性
     *  修改普通的成员属性
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_changeName(JNIEnv *env, jobject thiz) {
    
        //1、获取class
        jclass j_class = env->GetObjectClass(thiz);
        //2、获取属性id
        //j_class、属性名称、属性签名
        jfieldID j_Id = env->GetFieldID(j_class, "name", "Ljava/lang/String;");
        //3、将属性转换成jni中的桥梁 string转换成jstring
        jstring j_name = static_cast<jstring>(env->GetObjectField(thiz, j_Id));
        //4、将jstring转换成c、c++ char*进行打印
        char *c_name = const_cast<char *>(env->GetStringUTFChars(j_name, NULL));
        //5、打印结果
        LOGD("在c中获取到的name:%s\n", c_name);
        //6、修改java 中name的值,先创建一个jstring
        jstring j_tem = env->NewStringUTF("dog");
        //7、修改
        env->SetObjectField(thiz, j_Id, j_tem);
    
        //测试获取修改int类型的数据
        jfieldID count_jfieldId = env->GetFieldID(j_class, "count", "I");
        jint j_count = env->GetIntField(thiz, count_jfieldId);
        int count = j_count;
    
        LOGD("在c中获取到的coun:%d\n", count);
    
    
    }
    /**
     * 实现在c中修改java中的静态的成员属性
     *
     *
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_changeAge(JNIEnv *env, jclass j_class) {
        //1、获取属性ID
        jfieldID jfield_Id = env->GetStaticFieldID(j_class, "age", "I");
        //2、java中的int age转换成桥梁 jint
        jint j_age = env->GetStaticIntField(j_class, jfield_Id);
        int c_age = j_age;
        //3、打印结果
        LOGD("在c中获取到的age:%d\n", c_age);
        //4、修改age的值
        jint temp = 100;
        env->SetStaticIntField(j_class, jfield_Id, temp);
    
    }
    
    /**
     * 实现c++调用java中的函数
     * 无返回值
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_callBack(JNIEnv *env, jobject thiz) {
        //1、获取方法id
        jclass j_class = env->GetObjectClass(thiz);
        jmethodID jmethod_Id = env->GetMethodID(j_class, "nativeToJava", "(ILjava/lang/String;)V");
        //2、执行方法
        jstring temp = env->NewStringUTF("hello world");
        jint number = 100;
        env->CallVoidMethod(thiz, jmethod_Id, number, temp);
    
    
    }
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_javaToNativeNoParams(JNIEnv *env, jobject thiz) {
        LOGD("Java调用C++中的函数");
        //1、获取方法id
        jclass j_class = env->GetObjectClass(thiz);
        jmethodID jmethod_Id = env->GetMethodID(j_class, "nativeToJava2",
                                                "(ILjava/lang/String;)Ljava/lang/String;");
        //2、执行方法
        jstring temp = env->NewStringUTF("hello world");
        jint number = 800;
        jstring jstring_temp = static_cast<jstring>(env->CallObjectMethod(thiz, jmethod_Id, number,
                                                                          temp));
    
        char *data = const_cast<char *>(env->GetStringUTFChars(jstring_temp, NULL));
        LOGD("C++调用Java拿到的值:%s\n", data);
        jstring result = env->NewStringUTF("java is pig");
        return result;
    
    }
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_javaToNativeParams(JNIEnv *env, jobject thiz, jint age,
                                                                jstring sex, jintArray array,
                                                                jobjectArray strs) {
        //1、基本数据类型
        LOGD("基本数据类型:%d", age);
        //2、字符串
        const char *sexStr = env->GetStringUTFChars(sex, NULL);
        LOGD("字符串:%s", sexStr);
        //3、操作int类型数组
        jint *jintArray = env->GetIntArrayElements(array, NULL);
        int length = env->GetArrayLength(array);
        //遍历指针
        for (int i = 0; i < length; ++i) {
            LOGD("遍历int类型数组:%d", *(jintArray + i));
        }
        //释放数组
        env->ReleaseIntArrayElements(array, jintArray, 0);
    
        //GetIntArrayElements和ReleaseIntArrayElements一一对应
    
        //4、操作对象,字符串数组
        int size = env->GetArrayLength(strs);
        for (int i = 0; i < size; ++i) {
            jstring jstringStr = static_cast<jstring>(env->GetObjectArrayElement(strs, i));
            //char*->jstring
            // env->NewStringUTF()
            //jstring->char*
            const char *str = env->GetStringUTFChars(jstringStr, NULL);
            LOGD("遍历String类型数组:%s", str);
            env->ReleaseStringUTFChars(jstringStr, str);
            //GetStringUTFChars和ReleaseStringUTFChars一一对应
    
        }
    
    
    }
    /**
     * java调用native传递对象
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_javaToNativeObj(JNIEnv *env, jobject thiz, jobject student,
                                                             jstring str) {
        jclass j_class = env->GetObjectClass(student);
        jstring j_string = env->NewStringUTF("daxiaa");
        //调用Student中的函数 setName
        jmethodID j_methodId = env->GetMethodID(j_class, "setName", "(Ljava/lang/String;)V");
        env->CallVoidMethod(student, j_methodId, j_string);
        //调用student中的静态方方达
        jmethodID j_static_methodId = env->GetStaticMethodID(j_class, "show", "()V");
        env->CallStaticVoidMethod(j_class, j_static_methodId);
    
        //调用getName 带返回值的函数
        jmethodID j_return_methodId = env->GetMethodID(j_class, "getName", "()Ljava/lang/String;");
        jstring jstringName = static_cast<jstring>(env->CallObjectMethod(student, j_return_methodId));
        const char *nameChar = env->GetStringUTFChars(jstringName, NULL);
        LOGD("调用Student对象中的getName函数返回值:%s", nameChar);
        env->ReleaseStringUTFChars(jstringName, nameChar);
    
    
    }
    /**
     *native创建java的Student对象
     */
    extern "C"
    JNIEXPORT jobject JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_nativeNewObj(JNIEnv *env, jobject thiz) {
        //1、通过包名+类名拿到Student的class
        char *calssName = "com/hvm/vender/jni_01/Student";
        jclass j_class = env->FindClass(calssName);
        jstring name = env->NewStringUTF("Pig");
    
        //创建对象 实例化此Student对象   C++ new Student
        //(1)创建对象方式1 AllocObject方式,只示例对象,不会调用构造函数
        jobject j_object_student = env->AllocObject(j_class);
        jmethodID j_methodId = env->GetMethodID(j_class, "setName", "(Ljava/lang/String;)V");
        //3、调用setName函数
        env->CallVoidMethod(j_object_student, j_methodId, name);
    
        //(2)创建对象方式3 NewObject 方式,会调用构造函数
        //获取构造方法的id
        jmethodID constructMethodId = env->GetMethodID(j_class, "<init>", "(Ljava/lang/String;I)V");
    
        jobject j_object_student2 = env->NewObject(j_class, constructMethodId, name,
                                                   150);///NewObject,会调用构造函数
        jmethodID j_getAgemethodId = env->GetMethodID(j_class, "getAge", "()I");
        jint age = env->CallIntMethod(j_object_student2, j_getAgemethodId);
    
        LOGD("调用Student对象中的getAge函数返回值:%d", age);
        //用完释放
        // env->DeleteLocalRef(j_class);
        //env->DeleteLocalRef(j_object_student2);
        //env->DeleteLocalRef(j_object_student);
    
        char *personClassName = "com/hvm/vender/jni_01/Person";
        jclass personCalss = env->FindClass(personClassName);
        jobject jobjectPerson = env->AllocObject(personCalss);
        jmethodID jmethodIdSetName = env->GetMethodID(personCalss, "setName", "(Ljava/lang/String;)V");
        //设置personName属性
        env->CallVoidMethod(jobjectPerson, jmethodIdSetName, env->NewStringUTF("person is pig"));
        //设置person属性
        jfieldID j_fieldIdPerson = env->GetFieldID(j_class, "person", "Lcom/hvm/vender/jni_01/Person;");
        env->SetObjectField(j_object_student2, j_fieldIdPerson, jobjectPerson);
    
    
        return j_object_student2;
    
    
    }
    jclass j_class;
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_globalReference(JNIEnv *env, jobject thiz) {
        //这种方式创建全局引用可行
        if (!j_class) {
            char *personClassName = "com/hvm/vender/jni_01/Person";
            jclass temp = env->FindClass(personClassName);
            j_class = static_cast<jclass>(env->NewGlobalRef(temp));
        }
        //不能用这种方式创建全局引用
    //    if (!j_class) {
    //        char *personClassName = "com/hvm/vender/jni_01/Person";
    //        j_class = env->FindClass(personClassName);
    //    }
    
        jmethodID jmethod_Id = env->GetMethodID(j_class, "<init>", "()V");
        jobject j_object = env->NewObject(j_class, jmethod_Id);
        LOGD("全局引用测试");
    
    }
    //extern 关键字声明,extern-lib.cpp中实现
    extern int age;
    extern void show();
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_hvm_vender_jni_101_MainActivity_extern(JNIEnv *env, jobject thiz) {
        show();
    }
    
    //
    // Created by mayn on 2022/8/27.
    //
    // NDK工具链里面的 log 库 引入过来
    #include <android/log.h>
    
    #define TAG "MainActivity_C"
    // ... 我都不知道传入什么  借助JNI里面的宏来自动帮我填充
    #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() {
        LOGD("extern %d", age);
    
    }
    
    

    源码

    https://gitee.com/daxiaa/jni-ndk.git

    相关文章

      网友评论

        本文标题:JNI-NDK(Java和Native的互相调用)

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