前言
在http://www./archives/1097中,我介紹了下了如何編寫一個簡單的Demo來讓native輸出hello world到j(luò)ava層,接下來本篇主要介紹Java層調(diào)用native方法,以及native層調(diào)用Java層的方法以及修改Java對象的屬性。
Java層調(diào)用native層的方法
其實(shí)在上一篇中已經(jīng)介紹了下如何調(diào)用native層方法,其實(shí)與調(diào)用普通的Java方法沒有什么區(qū)別,只是方法由native層實(shí)現(xiàn)的而已。
首先我們在Java類中編寫native方法,然后使用上一篇博客中介紹的方法在native層實(shí)現(xiàn)之。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
package org.ndk.ndkfirst; public class NDKTest { private static final String TAG = "NDKTest"; static { System.loadLibrary("ndk-test-lib"); } private String mString = "Ndk test string"; /** * 在native中進(jìn)行加法運(yùn)算 */ public native int calcInNative(int num1, int num2); } |
|
#include <jni.h> extern "C" { JNIEXPORT jint JNICALL Java_org_ndk_ndkfirst_NDKTest_calcInNative(JNIEnv *env, jobject obj, jint num1, jint num2) { return num1 + num2; } } |
我們可以看到,只是將加法運(yùn)算在native層實(shí)現(xiàn)而已,調(diào)用native方法與普通Java方法一樣的,調(diào)用代碼如下。
|
NDKTest nDKTest = new NDKTest(); nDKTest.calcInNative(1, 2); |
native層調(diào)用Java方法
Java數(shù)據(jù)類型的對應(yīng)
在native層調(diào)用Java方法,首先得知道Java類型與native的對應(yīng)關(guān)系,以及Java類型以及方法在native中的簽名格式。
對于基礎(chǔ)類型,對應(yīng)關(guān)系如下。這些數(shù)據(jù)類型的定義全在jni.h中
|
#define JNI_FALSE 0 #define JNI_TRUE 1 typedef unsigned char jboolean; /* unsigned 8 bits */ typedef signed char jbyte; /* signed 8 bits */ typedef unsigned short jchar; /* unsigned 16 bits */ typedef short jshort; /* signed 16 bits */ typedef int jint; /* signed 32 bits */ typedef long long jlong; /* signed 64 bits */ typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ |
對于非基礎(chǔ)類型,比如String等,Array等統(tǒng)一對應(yīng)jobject對象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
class _jobject {}; class _jclass : public _jobject {}; class _jstring : public _jobject {}; class _jarray : public _jobject {}; class _jobjectArray : public _jarray {}; class _jbooleanArray : public _jarray {}; class _jbyteArray : public _jarray {}; class _jcharArray : public _jarray {}; class _jshortArray : public _jarray {}; class _jintArray : public _jarray {}; class _jlongArray : public _jarray {}; class _jfloatArray : public _jarray {}; class _jdoubleArray : public _jarray {}; class _jthrowable : public _jobject {}; typedef _jobject* jobject; typedef _jclass* jclass; typedef _jstring* jstring; typedef _jarray* jarray; typedef _jobjectArray* jobjectArray; typedef _jbooleanArray* jbooleanArray; typedef _jbyteArray* jbyteArray; typedef _jcharArray* jcharArray; typedef _jshortArray* jshortArray; typedef _jintArray* jintArray; typedef _jlongArray* jlongArray; typedef _jfloatArray* jfloatArray; typedef _jdoubleArray* jdoubleArray; typedef _jthrowable* jthrowable; typedef _jobject* jweak; |
Java類型以及方法簽名
在native中調(diào)用Java層的方法以及屬性的時候需要使用相應(yīng)的簽名表示方法以及屬性,下面就來介紹下基礎(chǔ)類型與引用類型的簽名表示方法。
從上面的表可以看到,基礎(chǔ)類型是使用一個大寫字母表示,對于數(shù)據(jù)則是使用[數(shù)組類型簽名,比如int[]數(shù)組的簽名為[I,一維數(shù)組是一個[,多維數(shù)組就是多個[[[,對于Java中的對象則是L類全路徑;,不要忘記結(jié)尾的;號,對于對于函數(shù)方法則是(參數(shù)一簽名參數(shù)二簽名...)返回值簽名,中間沒有空格。
比如上面的 public native int calcInNative(int num1, int num2);方法的前面為(II)I,其他的類似。
native調(diào)用Java中的方法
首先我們得知道,任何jni的方法都需要通過JNIEnv *env參數(shù)去調(diào)用
|
//C++形式 env->方法名 //C形式 (*env)->方法名 |
native層調(diào)用Java層方法以及屬性的時候類似于使用反射去調(diào)用Java的方法,基本分為如下幾步。
1、獲取jclass對象(類似于Java中的Class對象)
|
//方法一,通過jobject對象直接獲取 jclass jclass1 = env->GetObjectClass(jobject); //方法二,通過類描述符獲取 //匿名類使用$號隔開,比如Build中定義的VERSION->android/os/Build$VERSION env->FindClass(類描述符); //這里不是Ljava/lang/String;的形式,只有在參數(shù)列表中需要這么寫 jclass cls = env->FindClass("java/lang/String"); |
2、獲取jmethodID或者jfieldID(類似于Java中的Method或者Field)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
//獲取非靜態(tài)方法 //參數(shù)const char *name 方法名 //參數(shù)const char *sig 方法簽名 jmethodID env->GetMethodID(jclass clazz, const char *name, const char *sig) //獲取靜態(tài)方法 jmethodID env->GetStaticMethodID(jclass clazz, const char *name, const char *sig) //獲取非靜態(tài)的成員變量 jfieldID env->GetFieldID(jclass clazz, const char *name, const char *sig) //獲取靜態(tài)的成員變量 jfieldID env->GetStaticFieldID(jclass clazz, const char *name, const char *sig) //獲取指定類型的成員變量,如下面一個 jboolean env->Get[Type]Field(jobject obj, jfieldID fieldID) jboolean env->GetBooleanField(jobject obj, jfieldID fieldID) //獲取指定類型的靜態(tài)成員變量,如下面一個 jboolean env->GetStatic[Type]Field(jclass clazz, jfieldID fieldID) jboolean env->GetStaticBooleanField(jclass clazz, jfieldID fieldID) |
3、調(diào)用相應(yīng)函數(shù)以及獲取/修改屬性值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
//調(diào)用Java方法 env->Call[Type]Method(...) jobject env->CallBooleanMethod(jobject obj, jmethodID methodID, ...) // 調(diào)用Java靜態(tài)方法 env->CallStatic[Type]Method(...) jboolean env->CallStaticBooleanMethod(jclass clazz, jmethodID methodID, ...) //獲取Java屬性值 env->Get[Type]Field() jint env->GetIntField(jobject obj, jfieldID fieldID) //獲取Java靜態(tài)屬性值 env->GetStatic[Type]Field() jboolean env->GetStaticBooleanField(jclass clazz, jfieldID fieldID) //設(shè)置Java屬性值 env->Set[Type]Field() void env->SetBooleanField(jobject obj, jfieldID fieldID, jboolean value) //設(shè)置Java靜態(tài)屬性值 env->SetStatic[Type]Field() void env->SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) |
native調(diào)用舉例
由于本篇博客篇幅已經(jīng)較長,所以這里只給出部分關(guān)鍵代碼。下面為Java層代碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
package org.ndk.ndkfirst; import android.util.Log; public class NDKTest { private static final String TAG = "NDKTest"; static { System.loadLibrary("ndk-test-lib"); } private String mString = "Ndk test string"; /** * 在native中調(diào)用java方法 */ public native void callJavaMetood(); /** * 在native中修改對象的字段 */ public native void modifyFiled(); /** * 輸出成員變量的值 */ public void outputString() { Log.i(TAG, "mString: " + mString); } /** * 在native中調(diào)用此方法 */ @Override public String toString() { Log.i(TAG, "toString: "); return mString; } } |
對應(yīng)的native層實(shí)現(xiàn)。
|
#include <jni.h> extern "C" { JNIEXPORT void JNICALL Java_org_ndk_ndkfirst_NDKTest_callJavaMetood(JNIEnv *env, jobject obj) { jclass jclass1 = env->GetObjectClass(obj); jmethodID methodID = env->GetMethodID(jclass1, "toString", "()Ljava/lang/String;"); env->CallObjectMethod(obj, methodID); } JNIEXPORT void JNICALL Java_org_ndk_ndkfirst_NDKTest_modifyFiled(JNIEnv *env, jobject obj) { jclass jclass1 = env->GetObjectClass(obj); jfieldID fieldID1 = env->GetFieldID(jclass1,"mString","Ljava/lang/String;"); env->SetObjectField(obj,fieldID1,env->NewStringUTF("native modify it")); } } |
Demo地址:github
|