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

本文适合有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 
![ndk-javap.png](http://upload-images.jianshu.io/upload_images/2212019-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 
#include 

#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(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类中的属性和方法)