我在博客上发表一些我的NDK学习心得,希望对大家能有帮助。 这一篇我们讲述如何在so中获取到Java对象属性
介绍
首先,之前写的文章中通过一个简单的例子从从Native中调用Java方法。
下面,我们要介绍的是如何在Native层中获取到Java对象属性
问题
首先,从Native层要操作Java对象的属性有两种方法:
1. 直接操作
2. 间接操作
来解释一下什么叫直接操作,即在Native层中直接获取到对象的属性,从而进行操作。间接操作是在Native层中操作相应对象的Get&Set方法来对属性进行操作,这个可以参考上一篇Native调用Java方法,原理是一样的。
继续从MyJni入手,定义一个变量n,其拥有Get&Set方法,如下所示:
public class MyJni {
......
int n = 10;
public void setN(int n) {
this.n = n;
}
public int getN() {
return n;
}
public native int nativeGetMyJniFieldN();
public native void nativeSetN(int n);
public native int nativeGetN();
......
}
实践
直接方法
在jni.h中:
定义了GetFieldID来获取对应类的属性ID
jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
定义GetXXXField SetXXXField函数,通过使用这个我们就可以来实现获取类中对应ID的属性调用方法。
jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID);
jchar (*GetCharField)(JNIEnv*, jobject, jfieldID);
jshort (*GetShortField)(JNIEnv*, jobject, jfieldID);
jint (*GetIntField)(JNIEnv*, jobject, jfieldID);
jlong (*GetLongField)(JNIEnv*, jobject, jfieldID);
jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID) __NDK_FPABI__;
jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID) __NDK_FPABI__;
void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat) __NDK_FPABI__;
void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble) __NDK_FPABI__;
Java层:
case R.id.button2:
Log.d("123","" + myJni.n);
Log.d("123", "nativeGetMyJniFieldN : " + myJni.nativeGetMyJniFieldN()); // 通过nativeGetMyJniField函数来获取n参数
break;
Native层:
/*
* Class: com_example_qiuyu_testhellojni_MyJni
* Method: nativeGetMyJniFieldN
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_qiuyu_testhellojni_MyJni_nativeGetMyJniFieldN
(JNIEnv * env, jobject thiz) {
jclass clazz = (*env)->GetObjectClass(env, thiz); // 先获取到对象的类,即myJni的类是MyJni
//jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
jfieldID myjni_n = (*env)->GetFieldID(env, clazz, "n", "I"); // 获取到该类的对象"n"的ID,类型是int
//jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
return (*env)->GetIntField(env, thiz, myjni_n); // 通过执行GetXXXField函数来获取
}
间接方法
间接调用则比较简单,调用Set函数可以设置属性值,Get函数可以获取属性值,直接看代码:
Java:
case R.id.button3:
Log.d("123","GET : " + myJni.nativeGetN()); // 先获取一下,输出
myJni.nativeSetN(11); // 设置n
Log.d("123","GET : " + myJni.nativeGetN()); // 输出n
break;
Native:
/*
* Class: com_example_qiuyu_testhellojni_MyJni
* Method: nativeSetN
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_example_qiuyu_testhellojni_MyJni_nativeSetN
(JNIEnv * env, jobject thiz, jint n) {
jclass clazz = (*env)->GetObjectClass(env, thiz);
jmethodID setN = (*env)->GetMethodID(env,clazz,"setN","(I)V");
(*env)->CallVoidMethod(env,thiz,setN,n);
}
/*
* Class: com_example_qiuyu_testhellojni_MyJni
* Method: nativeGetN
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_qiuyu_testhellojni_MyJni_nativeGetN
(JNIEnv * env, jobject thiz) {
jclass clazz = (*env)->GetObjectClass(env, thiz);
jmethodID getN = (*env)->GetMethodID(env,clazz,"getN","()I");
return (*env)->CallIntMethod(env,thiz,getN);
}
源码
Github : https://github.com/QyMars/AndroidNativeCode
总结
这样,实现了简单C获取Java对象的属性,这样,Native层对于Java的操作更加灵活,好用。