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 <android/log.h>
![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 <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;
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.在C/C++中实现本地方法 生成C/C++头文件之后,你就需要写头文件对应的本地方法。注意:所有的本地方法的第...
    JayQiu阅读 2,400评论 0 3
  • 实例变量和静态变量在上一章中我们学习到了如何在本地代码中访问任意 Java 类中的静态方法和实例方法,本章我们也通...
    程序员学园阅读 388评论 0 0
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,764评论 18 399
  • 1.class与id的使用场景 id是对标签的标识,页面内唯一,对应CSS的选择符是“#”; class是对一类元...
    upup_dayday阅读 210评论 0 0
  • 一 谢斌和柳花落策马疾驰,行进间不忘讨论此前的惊险遭遇。 柳花落被三人追杀,侥幸生还。 谢斌惊讶道:你当真不知道他...
    梦鸭石阅读 706评论 0 2