在前面的章节JNI/NDK入门指南之JNI访问数组中讲解了JNI对基本类型数组和各种引用类型数组的访问。今天我们继续向JNI的知识海洋进军讲解C/C++通过JNI访问Java实例属性和类静态属性的处理。本章内容有点多哦!
通过前面的正佳我们知道了如何通过 JNI 函数来访问Native C/C++中的基本数据类型、字符串和数组这些数据类型。接下来我们接着学习C/C++本地代码如何通过JNI访问Java类中任意对象的属性交互。比如本地代码调用 Java 层某个对象的变量或者Java类的静态变量。静态变量也称为类变量(属性),在所有实例对象中共享同一份数据,可以直接通过【类名.变量名】来访问。实例变量也称为成员变量(属性),每个实例都拥有一份实例变量数据的拷贝,它们之间修改后的数据互不影响。下面让我们从JNI的访问对象操作函数开始,一步步带领读者攻破C/C++通过JNI访问Java实例变量和静态变量。好了,开车了。
好了有了前面知识的铺垫,下面让我们来看看JNIEnv为我们提供了那些常见Java对象属性处理函数。
函数原型: jfieldID GetFieldID (JNIEnv *env, jclass clazz, const char *name, const char *sig)
函数功能: 返回类的实例(非静态)域的属性 ID。该域由其名称及签名指定。访问属性系列函数Get
参数:
返回值:属性ID。如果操作失败,则返回NULL。
抛出:
这里的PrimitiveType指代的是一系列的JNI数据类型,如果是引用类型的话PrimitiveType用Object替代。
函数原型: NativeType Get
函数功能: 该函数集用于返回Java类的实例(非静态)域的值。要访问的域由通过调用GetFieldID() 而得到的域 ID 指定。
使用说明: 在下表中会将特定的基本类型数组构造函数及其返回值一一对应。在实际应用中把PrimitiveType替换为某个实际的基本类型数据类型,然后再将NativeType替换成对应的JNI Native Type即可。譬如我们以int型来说明,那么其对应的函数为:
jint GetIntField(JNIEnv * env, jobject obj, jfieldID fieldID);
参数:
返回值:属性的内容。
函数集表格:
Get |
NativeType JNI本地类型 |
---|---|
GetObjectField() | jobject |
GetBooleanField() | jboolean |
GetByteField() | jbyte |
GetCharField() | jchar |
GetShortField() | jshort |
GetIntField() | jint |
GetLongField() | jlong |
GetFloatField() | jfloat |
GetDoubleField() | jdouble |
这里的PrimitiveType指代的是一系列的JNI数据类型,如果是引用类型的话PrimitiveType用Object替代。
函数原型: void Set
函数功能: 该函数集用于设置对象的实例(非静态)属性的值。要访问的属性由通过调用GetFieldID() 而得到的属性 ID指定。
使用说明: 在下表中会将特定的基本类型数组构造函数及其返回值一一对应。在实际应用中把PrimitiveType替换为某个实际的基本类型数据类型,然后再将NativeType替换成对应的JNI Native Type即可。譬如我们以int型来说明,那么其对应的函数为:
void SetIntField(JNIEnv * env, jobject obj, jfieldID fieldID, jint value);
参数:
函数集表格:
Set |
NativeType JNI本地类型 |
---|---|
SetObjectField() | jobject |
SetBooleanField() | jboolean |
SetByteField() | jbyte |
SetCharField() | jchar |
SetShortField() | jshort |
SetIntField() | jint |
SetLongField() | jlong |
SetFloatField() | jfloat |
SetDoubleField() | jdouble |
在前面的章节我们讲解了访问和修改Java对象属性的处理函数,下面让我们来看看JNIEnv为我们提供了那些常见Java类对象属性处理函数。
函数原型: jfieldID GetStaticFieldID (JNIEnv *env, jclass clazz, const char *name, const char *sig)
函数功能: 返回类对象(静态)域的属性 ID。该域由其名称及签名指定。访问属性系列函数GetStatic
参数:
返回值:属性ID。如果操作失败,则返回NULL。
抛出:
这里的PrimitiveType指代的是一系列的JNI数据类型,如果是引用类型的话PrimitiveType用Object替代。
函数原型: NativeType GetStatic
函数功能: 该函数集用于返回Java类对象(静态)域的值。要访问的域由通过调用GetStaticFieldID() 而得到的域 ID 指定。
使用说明: 在下表中会将特定的基本类型数组构造函数及其返回值一一对应。在实际应用中把PrimitiveType替换为某个实际的基本类型数据类型,然后再将NativeType替换成对应的JNI Native Type即可。譬如我们以int型来说明,那么其对应的函数为:
jint GetStaticIntField(JNIEnv * env, jclass clazz, jfieldID fieldID);
参数:
返回值:静态属性的内容。
函数集表格:
GetStatic |
NativeType JNI本地类型 |
---|---|
GetStaticObjectField() | jobject |
GetStaticBooleanField() | jboolean |
GetStaticByteField() | jbyte |
GetStaticCharField() | jchar |
GetStaticShortField() | jshort |
GetStaticIntField() | jint |
GetStaticLongField() | jlong |
GetStaticFloatField() | jfloat |
GetStaticDoubleField() | jdouble |
这里的PrimitiveType指代的是一系列的JNI数据类型,如果是引用类型的话PrimitiveType用Object替代。
函数原型: void SetStatic
函数功能: 该函数集用于设置Java类对象(静态)属性的值。要访问的属性由通过调用GetStaticFieldID() 而得到的属性 ID指定。
使用说明: 在下表中会将特定的基本类型数组构造函数及其返回值一一对应。在实际应用中把PrimitiveType替换为某个实际的基本类型数据类型,然后再将NativeType替换成对应的JNI Native Type即可。譬如我们以int型来说明,那么其对应的函数为:
void SetStaticIntField(JNIEnv * env, jobject obj, jfieldID fieldID, jint value);
参数:
函数集表格:
SetStatic |
NativeType JNI本地类型 |
---|---|
SetStaticObjectField() | jobject |
SetStaticBooleanField() | jboolean |
SetStaticByteField() | jbyte |
SetStaticCharField() | jchar |
SetStaticShortField() | jshort |
SetStaticIntField() | jint |
SetStaticLongField() | jlong |
SetStaticFloatField() | jfloat |
SetStaticDoubleField() | jdouble |
可以看出它们与实例属性的唯一区别在于第二个参数jclass classzz代表的是类引用,而不是类实例。
前面的章节,我们将JNI访问Java实例属性和类静态属性中有关访问函数集,一网打净了(当然是夸张说法了)。说得再多不练,都是纸上谈兵,下面我们来实战一把,跟紧我要开战了,可别走丢了。
好了前面的理论知识我们已经OK了,那么接下来都懂的必须来点实战的东西,不能光说不练。让我带领读者来看看怎么通过JNI访问和修改Java实例属性。
Java端代码:
Java实例所属类JNIFieldClass.java代码:
package com.xxx.jni.field;
public class JNIFieldClass {
private String mString = "Hello JNI, this is normal string !";
private int mInt = 1;
@Override
public String toString() {
return "JNIFieldClass [mString=" + mString + ", mInt=" + mInt + "]";
}
}
Java端Native方法定义JNIAccessFieldManager.java代码:
package com.xxx.jni.field;
/***
*
* C/C++访问Java类的实例变量和静态变量
*
*/
public class JNIAccessFieldManager {
public native void accessInstanceFiled(JNIFieldClass mJniFieldClass);
static {
System.loadLibrary("accesfield");// 加载so库
}
}
Java端测试代码:
private void operateInstanceFiled(){
JNIFieldClass mJniFieldClass = new JNIFieldClass();
Log.e("ACCESS_FIELD","operateInstanceFiled before : " + mJniFieldClass.toString());
JNIAccessFieldManager mAccessFieldManager = new JNIAccessFieldManager();
mAccessFieldManager.accessInstanceFiled(mJniFieldClass);
Log.e("ACCESS_FIELD","operateInstanceFiled after : " + mJniFieldClass.toString());
}
JNI端代码:
Java中Native方法对应com_xxx_jni_field_JNIAccessFieldManager.h代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_xxx_jni_field_JNIAccessFieldManager */
#ifndef _Included_com_xxx_jni_field_JNIAccessFieldManager
#define _Included_com_xxx_jni_field_JNIAccessFieldManager
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_xxx_jni_field_JNIAccessFieldManager
* Method: accessInstanceFiled
* Signature: (Lcom/xxx/jni/field/JNIFieldClass;)V
*/
JNIEXPORT void JNICALL Java_com_xxx_jni_field_JNIAccessFieldManager_accessInstanceFiled
(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif
对应的com_xxx_jni_field_JNIAccessFieldManager.cpp代码如下:
#include "com_xxx_jni_field_JNIAccessFieldManager.h"
#include
#include
#include
#include
#include
#define TAG "ACCESS_FIELD"
#define LOGE(TAG,...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
//将jstring转换成char *
char* jstringToNative(JNIEnv *env, jstring jstr)
{
if ((env)->ExceptionCheck() == JNI_TRUE || jstr == NULL)
{
(env)->ExceptionDescribe();
(env)->ExceptionClear();
//printf("jstringToNative函数转换时,传入的参数str为空");
return NULL;
}
jbyteArray bytes = 0;
jthrowable exc;
char *result = 0;
if ((env)->EnsureLocalCapacity(2) < 0)
{
return 0; /* out of memory error */
}
jclass jcls_str = (env)->FindClass("java/lang/String");
jmethodID MID_String_getBytes = (env)->GetMethodID(jcls_str, "getBytes", "()[B");
bytes = (jbyteArray)(env)->CallObjectMethod(jstr, MID_String_getBytes);
exc = (env)->ExceptionOccurred();
if (!exc)
{
jint len = (env)->GetArrayLength( bytes);
result = (char *)malloc(len + 1);
if (result == 0)
{
//JNU_ThrowByName( "java/lang/OutOfMemoryError", 0);
(env)->DeleteLocalRef(bytes);
return 0;
}
(env)->GetByteArrayRegion(bytes, 0, len, (jbyte *)result);
result[len] = 0; /* NULL-terminate */
}
else
{
(env)->DeleteLocalRef( exc);
}
(env)->DeleteLocalRef( bytes);
return (char*)result;
}
//将char * 转换成 jstring
jstring nativeTojstring( JNIEnv* env,const char* str )
{
//定义java String类 strClass
jclass strClass = (env)->FindClass("java/lang/String");
//获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID ctorID = (env)->GetMethodID( strClass, "" , "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray( (jsize)strlen(str));
//将char* 转换为byte数组
(env)->SetByteArrayRegion( bytes, 0, (jsize)strlen(str), (jbyte*)str);
//设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = (env)->NewStringUTF( "utf-8");
//将byte数组转换为java String,并输出
return (jstring)(env)->NewObject( strClass, ctorID, bytes, encoding);
}
/*
* Class: com_xxx_jni_field_JNIAccessFieldManager
* Method: accessInstanceFiled
* Signature: (Lcom/xxx/jni/field/JNIFieldClass;)V
*/
JNIEXPORT void JNICALL Java_com_xxx_jni_field_JNIAccessFieldManager_accessInstanceFiled
(JNIEnv * env, jobject object, jobject object_in)
{
jclass clazz;//JNIFieldClass类引用
jfieldID mString_fieldID;//JNIFieldClass类对象变量mString属性ID
jfieldID mInt_fieldID;//JNIFieldClass类对象变量mInt属性ID
/******获取JNIFieldClass类实例变量mString的值并修改********/
//1.通过JNIFieldClass类实例object_in获取Class的引用
clazz = env->GetObjectClass(object_in);
if(clazz == NULL)
{
LOGE(TAG,"GetObjectClass failed\n");
return;
}
//2.获取JNIFieldClass类的实例变量mString的属性ID
//其中第二参数是变量的名称,第三个参数是变量类型的描述符
mString_fieldID = env->GetFieldID(clazz, "mString", "Ljava/lang/String;");
if(mString_fieldID == NULL)
{
LOGE(TAG,"GetFieldID mString failed\n");
return;
}
//3.获取JNIFieldClass类实例变量mString的值,并打印出来
jstring j_string = (jstring)env->GetObjectField(object_in, mString_fieldID);
char * buf = jstringToNative(env, j_string);
LOGE(TAG,"object_in.mString : %s\n", buf);
free(buf);
//4.修改类实例变量mStriing的值
char * buf_out = "Hello Java, I am JNI!";
env->SetObjectField(object_in, mString_fieldID, nativeTojstring(env, buf_out));
//5.释放局部引用
env->DeleteLocalRef(j_string);
/******获取JNIFieldClass类实例int型变量mInt的值并修改********/
//6.获取JNIFieldClass类实例int型变量mString的属性ID
mInt_fieldID = env->GetFieldID(clazz, "mInt", "I");
if(mInt_fieldID == NULL)
{
LOGE(TAG,"GetFieldID mInt failed\n");
return;
}
//7.获取JNIFieldClass实例变量mInt的值
jint mInt = env->GetIntField(object_in, mInt_fieldID);
LOGE(TAG,"object_in.mInt : %d\n", mInt);
//8.修改JNIFieldClass实例变量mInt的值
env->SetIntField(object_in, mInt_fieldID, 100);
//9.删除局部引用,即对JNIFieldClass的类引用
env->DeleteLocalRef(clazz);
}
运行演示:
λ adb logcat -s ACCESS_FIELD
--------- beginning of main
--------- beginning of system
12-26 08:22:33.111 8575 8575 E ACCESS_FIELD: operateInstanceFiled before : JNIFieldClass [mString=Hello JNI, this is normal string !, mInt=1]
12-26 08:22:33.116 8575 8575 I ACCESS_FIELD: object_in.mString : Hello JNI, this is normal string !
12-26 08:22:33.117 8575 8575 I ACCESS_FIELD: object_in.mInt : 1
12-26 08:22:33.117 8575 8575 E ACCESS_FIELD: operateInstanceFiled after : JNIFieldClass [mString=Hello Java, I am JNI!, mInt=100]
案例分析:
在本例中,Java类JNIAccessFieldManager中定义了一个accessInstanceFiled的Native方法,参数类型是JNIFieldClass的实例,在其本地方法中我们会通过该实例进行对Java实例属性的访问和修改一系列操作。下面让我们对本地代码以一一分析。
获取JNIFieldClass类实例变量mString的值并修改:
在该部分代码中我们会通过JNI系列函数获取Java类实例属性的值并修改,然后将修改前后的值打印出来。
(1) 通过JNIFieldClass类对象object_in获取Class的引用,可能获取到的为NULL,必须加上异常判断。
(2) 调用函数GetFieldID获取JNIFieldClass类的实例变量mString的属性ID,其中第二参数是变量的名称,第三个参数是变量类型的描述符。
(3) 调用GetObjectField获取JNIFieldClass类实例变量mString的值,并打印出来。
(4) 调用SetObjectField修改类实例变量mStriing的值。
(5) 调用DeleteLocalRef释放局部引用。
获取JNIFieldClass类实例int型变量mInt的值并修改:
(1) 调用GetFieldID获取JNIFieldClass类实例int型变量mString的属性ID。
(2) 调用GetIntField获取JNIFieldClass实例变量mInt的值。
(3) 调用SetIntField修改JNIFieldClass实例变量mInt的值。
(4) 最后调用DeleteLocalRef删除局部引用,即对JNIFieldClass的类引用
在前面的章节带领读者见证了JNI访问和修改Java实例属性,那么接下来让我带领读者来看看怎么通过JNI访问和修改Java类对象静态属性。
Java端代码:
Java实例所属类JNIFieldClass.java代码:
package com.xxx.jni.field;
public class JNIFieldClass {
private static String mStaticString = "Hello JNI, this is static string !";
private static int mStaticInt = 0;
public static String toStaticString() {
return "JNIFieldClass [mStaticString=" + mStaticString + ", mStaticInt=" + mStaticInt + "]";
}
}
Java端Native方法定义JNIAccessFieldManager.java代码:
package com.xxx.jni.field;
/***
*
* C/C++访问Java类的实例变量和静态变量
*
*/
public class JNIAccessFieldManager {
public native void accessStaticField();
static {
System.loadLibrary("accesfield");// 加载so库
}
}
Java端测试代码:
private void operateStaticFiled(){
Log.e("ACCESS_FIELD","operateStaticFiled before : " + JNIFieldClass.toStaticString());
JNIAccessFieldManager mAccessFieldManager = new JNIAccessFieldManager();
mAccessFieldManager.accessStaticField();
Log.e("ACCESS_FIELD","operateStaticFiled after : " + JNIFieldClass.toStaticString());
}
JNI端代码:
Java中Native方法对应com_xxx_jni_field_JNIAccessFieldManager.h代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_xxx_jni_field_JNIAccessFieldManager */
#ifndef _Included_com_xxx_jni_field_JNIAccessFieldManager
#define _Included_com_xxx_jni_field_JNIAccessFieldManager
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_xxx_jni_field_JNIAccessFieldManager
* Method: accessStaticField
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_xxx_jni_field_JNIAccessFieldManager_accessStaticField
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
对应的com_xxx_jni_field_JNIAccessFieldManager.cpp代码如下:
#include "com_xxx_jni_field_JNIAccessFieldManager.h"
#include
#include
#include
#include
#include
#define TAG "ACCESS_FIELD"
#define LOGE(TAG,...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
//将jstring转换成char *
char* jstringToNative(JNIEnv *env, jstring jstr)
{
if ((env)->ExceptionCheck() == JNI_TRUE || jstr == NULL)
{
(env)->ExceptionDescribe();
(env)->ExceptionClear();
//printf("jstringToNative函数转换时,传入的参数str为空");
return NULL;
}
jbyteArray bytes = 0;
jthrowable exc;
char *result = 0;
if ((env)->EnsureLocalCapacity(2) < 0)
{
return 0; /* out of memory error */
}
jclass jcls_str = (env)->FindClass("java/lang/String");
jmethodID MID_String_getBytes = (env)->GetMethodID(jcls_str, "getBytes", "()[B");
bytes = (jbyteArray)(env)->CallObjectMethod(jstr, MID_String_getBytes);
exc = (env)->ExceptionOccurred();
if (!exc)
{
jint len = (env)->GetArrayLength( bytes);
result = (char *)malloc(len + 1);
if (result == 0)
{
//JNU_ThrowByName( "java/lang/OutOfMemoryError", 0);
(env)->DeleteLocalRef(bytes);
return 0;
}
(env)->GetByteArrayRegion(bytes, 0, len, (jbyte *)result);
result[len] = 0; /* NULL-terminate */
}
else
{
(env)->DeleteLocalRef( exc);
}
(env)->DeleteLocalRef( bytes);
return (char*)result;
}
//将char * 转换成 jstring
jstring nativeTojstring( JNIEnv* env,const char* str )
{
//定义java String类 strClass
jclass strClass = (env)->FindClass("java/lang/String");
//获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID ctorID = (env)->GetMethodID( strClass, "" , "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray( (jsize)strlen(str));
//将char* 转换为byte数组
(env)->SetByteArrayRegion( bytes, 0, (jsize)strlen(str), (jbyte*)str);
//设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = (env)->NewStringUTF( "utf-8");
//将byte数组转换为java String,并输出
return (jstring)(env)->NewObject( strClass, ctorID, bytes, encoding);
}
/*
* Class: com_xxx_jni_field_JNIAccessFieldManager
* Method: accessStaticField
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_xxx_jni_field_JNIAccessFieldManager_accessStaticField
(JNIEnv * env, jobject object)
{
jclass clazz;//JNIFieldClass类引用
jfieldID mStaticString_fieldID;//JNIFieldClass类静态变量mStaticString属性ID
jfieldID mStaticInt_filedID;//JNIFieldClass类静态变量StaticInt属性ID
/******获取JNIFieldClass类静态变量mStaticString的值并修改********/
//1.通过FindClass获取JNIFieldClass的类引用
clazz = env->FindClass("com/xxx/jni/field/JNIFieldClass");
if(clazz == NULL)
{
LOGE(TAG,"FindClass failed\n");
return;
}
//2.获取JNIFieldClass类静态变量mStaticString的属性ID
mStaticString_fieldID = env->GetStaticFieldID(clazz, "mStaticString", "Ljava/lang/String;");
if(mStaticString_fieldID == NULL)
{
LOGE(TAG,"mStaticString_fieldID mStaticString failed\n");
return;
}
//3.获取JNIFieldClass类静态变量mStaticString的值并打印
jstring j_string = (jstring)env->GetStaticObjectField(clazz, mStaticString_fieldID);
char * buf = jstringToNative(env, j_string);
LOGE(TAG,"JNIFieldClass.mString : %s\n", buf);
free(buf);
//4.修改JNIFieldClass类静态变量mStaticString的值
char * buf_out = "Hello Java, I am JNI!";
env->SetStaticObjectField(clazz, mStaticString_fieldID, nativeTojstring(env, buf_out));
//5.释放局部引用
env->DeleteLocalRef(j_string);
/******获取JNIFieldClass类静态变量mStaticInt的值并修改********/
//6.获取JNIFieldClass类静态变量mStaticInt的属性ID
mStaticInt_filedID = env->GetStaticFieldID(clazz, "mStaticInt", "I");
if(mStaticInt_filedID == NULL)
{
LOGE(TAG,"GetStaticFieldID mStaticInt failed\n");
return;
}
//7.获取JNIFieldClass类静态变量mStaticInt的值并打印
jint mInt = env->GetStaticIntField(clazz, mStaticInt_filedID);
LOGE(TAG,"JNIFieldClass.mInt : %d\n", mInt);
//8.修改JNIFieldClass类静态变量mStaticInt的值
env->SetStaticIntField(clazz, mStaticInt_filedID, 1000);
//9.删除局部引用,即对JNIFieldClass的类引用
env->DeleteLocalRef(clazz);
}
运行演示:
λ aadb logcat -s ACCESS_FIELD
--------- beginning of main
--------- beginning of system
12-26 09:27:58.422 8733 8733 E ACCESS_FIELD: operateStaticFiled before : JNIFieldClass [mStaticString=Hello JNI, this is static string !, mStaticInt=0]
12-26 09:27:58.426 8733 8733 I ACCESS_FIELD: JNIFieldClass.mString : Hello JNI, this is static string !
12-26 09:27:58.426 8733 8733 I ACCESS_FIELD: JNIFieldClass.mInt : 0
12-26 09:27:58.426 8733 8733 E ACCESS_FIELD: operateStaticFiled after : JNIFieldClass [mStaticString=Hello Java, I am JNI!, mStaticInt=1000]
案例分析:
在本例中,Java类JNIAccessFieldManager中定义了一个accessStaticField的Native方法,可以看到我们这里是没有传入任何参数的,因为我们是对类对象做访问不需要它的实例。下面让我们对本地代码以一一分析。
获取JNIFieldClass类静态变量mStaticString的值并修改:
在该部分代码中我们会通过JNI系列函数获取Java类对象静态属性的值并修改,然后将修改前后的值打印出来。
(1) 通过FindClass获取JNIFieldClass的类引用,可能获取到的为NULL,必须加上异常判断。
(2) 调用函数GetStaticFieldID获取JNIFieldClass类静态变量mStaticString的属性ID。
(3) 调用GetStaticObjectField获取JNIFieldClass类静态变量mStaticString的值并打印。
(4) 调用SetStaticObjectField修改JNIFieldClass类静态变量mStaticString的值。
(5) 调用DeleteLocalRef释放局部引用。
获取JNIFieldClass类静态变量mStaticInt的值并修改:
(1) 调用GetStaticFieldID获取JNIFieldClass类静态变量mStaticInt的属性ID。
(2) 调用GetStaticIntField获取JNIFieldClass类静态变量mStaticInt的值并打印。
(3) 调用SetStaticIntField修改JNIFieldClass类静态变量mStaticInt的值。
(4) 最后调用DeleteLocalRef删除局部引用,即对JNIFieldClass的类引用
好了到这里就解析完成了。
大家对前面的实例有没有一个疑问呢,为啥我们通过JNI能访问Java类实例私有属性和Java类对象私有静态属性呢。这是为什么呢?
public class JNIFieldClass {
private static String mStaticString = "Hello JNI, this is static string !";//私有
private String mString = "Hello JNI, this is normal string !";//私有
private static int mStaticInt = 0;//私有
private int mInt = 1;//私有
}
由于 JNI 函数是直接操作虚拟机中的数据结构,不受 Java 访问修饰符的限制。即,在本地代码中可以调用JNI 函数可以访问 Java 对象中的非 public 属性和方法。这也为操作Java对象中private的属性或者方法的一种思路。
前面的知识讲解都是为了引出最后的结论,下面让我们小结一把:
JNI访问和修改Java实例属性操作步聚:
各位读者看官朋友们,关于C/C++通过JNI访问Java实例属性和类静态属性就告一段落了。本篇几乎将JNI中访问Java实例属性和类静态属性各种情况都讲到了,只要仔细阅读本章,应该以后没有访问Java实例属性和类静态属性问题能难住各位了。在最后麻烦读者朋友们如果本篇对你有帮助,关注和点赞一下,当然如果有错误和不足的地方也可以拍砖。