本文适合有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
如下图。
![](https://img.haomeiwen.com/i2212019/f83261e6214d7a25.png)
将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;
}
备注:字段、方法的签名参考获取字段、方法签名
网友评论