美文网首页NDK/JNI
C/C++访问Java类中的属性和方法

C/C++访问Java类中的属性和方法

作者: LinuxPara | 来源:发表于2017-10-17 15:55 被阅读16次

    本文适合有C/C++基础,会NDK基本配置
    新工程默认会创建一个native-lib动态库及源文件,我这里用不到所以先删了(删除cpp中的native-lib.cpp,删除CMakeList文件中native-lib模块相关)。
    搞定后开始撸码。
    1. 新建java类NativeTest代码如下:

    public class NativeTest {
        static {
            //加载动态库
            System.loadLibrary("native-test");
        }
        //c要修改的字段
        public String name;
        
        public native void cUpdateName();
    }
    

    2.javah命令生成头文件
    进入java目录执行javah -o NativeTest.h com.linuxchen.nativetest.NativeTest如下图。

    javah生成头文件
    将NativeTest.h移动带cpp目录下,并创建NativeTest.cpp实现头文件中的方法。此时cpp中的代码如下:
    #include "NativeTest.h"
    #include <android/log.h>
    ![ndk-javap.png](https://img.haomeiwen.com/i2212019/ed7a58f31a47df12.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    
    #define LOGT "JNI"
    #define LOGI(format,...) __android_log_print(ANDROID_LOG_INFO,LOGT,format,##__VA_ARGS__)
    /*
     * Class:     com_linuxchen_nativetest_NativeTest
     * Method:    cUpdateName
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_linuxchen_nativetest_NativeTest_cUpdateName
      (JNIEnv *env, jobject obj){
        LOGI("test");
    }
    

    3.准备工作做完,开始正式编码
    C/C++获取java类中的字段过程类似java中的反射,需要以下几步:
    备注:JNIEXPORT、JNICALL、JNIEnv、jobject解释可参考JNI函数命名规则及分析

    3.1 C/C++操作Java字段

    3.1.1 获取类对象(jclass)
    env->GetObjectClass(jobj)
    3.1.2 获取字段ID (jfieldID)
    env->GetStaticFieldID(clazz,"age","I")//静态字段ID
    env->GetFieldID(clazz,"age","I")//普通字段ID
    3.1.3 根据字段ID获取字段
    env->GetStaticIntField(clazz,staticFieldID)//获取静态字段的值
    env->GetIntField(clazz,fieldID)//获取普通字段的值
    ...
    env->GetStaticObjectField(clazz,staticFieldID)//获取静态字段的值
    env->GetObjectField(clazz,fieldID)//获取普通字段的值
    3.4. 修改字段的值
    env->SetStaticIntField(clazz,staticFieldID,22)//设置java类中静态字段的值
    env->SetIntField(clazz,fieldID,22)//设置java类中普通字段的值
    ...
    j_str = env->NewStringUTF("abc");
    env->SetStaticObjectField(clazz,staticFieldID,j_str)//设置java类中静态字段的值
    j_str = env->NewStringUTF("abc");
    env->SetObjectField(clazz,fieldID,j_str)//设置java类中普通字段的值

    修改普通字段代码如下:

    #include "NativeTest.h"
    #include <android/log.h>
    #include <string.h>
    
    #define LOGT "JNI"
    #define LOGI(format,...) __android_log_print(ANDROID_LOG_INFO,LOGT,format,##__VA_ARGS__)
    /*
     * Class:     com_linuxchen_nativetest_NativeTest
     * Method:    cUpdateName
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_linuxchen_nativetest_NativeTest_cUpdateName
      (JNIEnv *env, jobject jobj){
        //1.获取类对象(class)
        jclass clazz = env->GetObjectClass(jobj);
        //2.获取字段ID 参数1:对象对应的class
        //            参数2:字段名称
        //            参数3:字段签名
        jfieldID fieldID = env->GetFieldID(clazz,"name","Ljava/lang/String;");
        //3.根据字段ID获取字段 如果字段类型是int则对应的方法为env->GetIntField。其他类型调用相对应的方法。
        jstring j_name = (jstring) env->GetObjectField(jobj, fieldID);
        //-------------------------获取java字段完成-------------------------------
        //创建新的字符更新java字段
        //4.创建新的字符串
        char* newStr = "jni test";
        //5.将jsting转成c字符串 jstring和c中char*不是同一种类型,在调用strcpy函数前需要将jstring转成c中的char*
        const char* c_name = env->GetStringUTFChars(j_name,NULL);
        //6.拷贝新字符串。 const_cast将const char* c_name的const关键字去掉。strcpy不接收const char*类型
        strcpy(const_cast<char *>(c_name),newStr);
        //7.将c_name转回jstring,为下一行代码做准备。
        j_name = env->NewStringUTF(c_name);
        //8.设置java类name字段的值
        env->SetObjectField(jobj,fieldID,j_name);
        return;
    }
    

    修改静态字段代码如下:

    /*
     * Class:     com_linuxchen_nativetest_NativeTest
     * Method:    cUpdateStaticAge
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_linuxchen_nativetest_NativeTest_cUpdateStaticAge
            (JNIEnv *env, jobject jobj){
        jclass clazz = env->GetObjectClass(jobj);
        jfieldID staticFieldID = env->GetStaticFieldID(clazz,"age","I");
        jint jage = env->GetStaticIntField(clazz,staticFieldID);
        LOGI("获得java静态字段age = %d",jage);
        env->SetStaticIntField(clazz,staticFieldID,22);
    }
    

    3.2 C/C++操作java方法

    3.2.1 获取类对象(jclass)
    env->GetObjectClass(jobj)
    3.2.2 获取方法ID
    env->GetMethodID(clazz,"generateUUID","()Ljava/lang/String;");//获取普通方法ID
    env->GetStaticMethodID(clazz,"generateUUID","()Ljava/lang/String;");//获取静态方法ID
    3.2.3 C/C++调用java方法
    env->CallObjectMethod(jobj,methodID)//调用普通方法。
    env->CallStaticObjectMethod(jobj,methodID)//调用静态方法
    ...(方法返回值是什么类型则Call...Method)

    调用普通方法代码如下:

    /*
     * Class:     com_linuxchen_nativetest_NativeTest
     * Method:    cUseJavaFunc
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_linuxchen_nativetest_NativeTest_cCallJavaFunc
            (JNIEnv *env, jobject jobj){
        //1.获取jclass
        jclass clazz = env->GetObjectClass(jobj);
        //2.获取methodID
        jmethodID methodID = env->GetMethodID(clazz,"generateUUID","()Ljava/lang/String;");
        //调用java方法获取返回值
        jstring uuid = (jstring)env->CallObjectMethod(jobj,methodID);
        const char* c_uuid = env->GetStringUTFChars(uuid,NULL);
        LOGI("%s",c_uuid);
        return uuid;
    }
    

    备注:字段、方法的签名参考获取字段、方法签名

    相关文章

      网友评论

        本文标题:C/C++访问Java类中的属性和方法

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