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

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 
#include 

// NDK工具链里面的 log 库 引入过来
#include 

#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(env->GetObjectField(thiz, j_Id));
    //4、将jstring转换成c、c++ char*进行打印
    char *c_name = const_cast(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(env->CallObjectMethod(thiz, jmethod_Id, number,
                                                                      temp));

    char *data = const_cast(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(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(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, "", "(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(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, "", "()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 

#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的互相调用))