此係列記錄Android NDK基礎開發知識,在Android NDK開發基礎篇(一)中介紹了NDK、JNI以及關係,包括AS創建JNI的項目及第一個函數解析,
目的
- Java類型和native類型的映射關係
- jobject
- jclass
- JNIEnv 的基本使用
用於參數解析:
Java_com_kpa_jnijavademo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject obj/* this */) {
return env->NewStringUTF(hello.c_str());
}
一、Java類型和native類型的映射關係
Java類型 | 本地類型 | JNI定義的別名 |
---|---|---|
int | long | jint/jsize |
short | short | jshort |
long | _int64 | jlong |
float | float | jfloat |
double | double | jdouble |
byte | signed char | jbyte |
boolean | unsigned char | jboolean |
char | unsigned char | jchar |
Object | _jobject* | jobject |
本地類型指的是在C/C++中的使用,JNI定義的別名是C/C++在JNI中的使用,不需要多記,後續回經常出現
二、 jobject
jobject 表示的是Java中的Object類型,在參數中有兩個不同的意義需要知道
- native方法是static 修飾的: 代表native方法的類的class對象的實例
- native方法不是static 修飾的:表示native方法的實例
三、jclass
jclass 表示Java的中的Class類
在下面的JNIEnv 中會解釋jclass 在C/C++中的集中獲取方法
四、 JNIEnv
JNIEnv 類型在native 中實際代表了Java在native中的Java環境 ,通過JNIEnv*指針可以對Java端的代碼進行操作,爲什麼JNI開發中首先要了解對Java的操作呢?
JNI作爲Java和C/C++的連接橋樑,自然能在C/C++中操作Java的對象,最起碼我們在C/C++中的一系列操作完成之後,將結果返回給Java就需要用到Java操作,以此問題還有很多。
既然是代表了Java環境,那首先有這幾個概念需要了解一下:
- 對象
- 屬性
- 方法
這是最起碼操作一個Java對象應該具備的要素
4.1在native中創建一個Java對象
Newobject
/**
*jclass : Class 對象(後續解釋怎麼獲取一個class對象)
*methodID: 方法ID
**/
jobject NewObject(jclass clazz, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
jobject result = functions->NewObjectV(this, clazz, methodID, args);
va_end(args);
return result;
}
jmethodID 在JNI中表示一個指向method的指針,創建對象時的method哪裏來呢? 沒錯就是構造方法
爲了後續更好的理解 先看一下jclass 的獲取方法
4.2 JNIEnv 中獲取jclass 的方法
- FindClass
// 通過類的全名獲取class 對象
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
eg:
// 獲取Java中Date 對象的Class對象
jclass date_clazz = env->FindClass("java/util/Date");
- GetObjectClass
// 通過native方法中的jobject對象獲取Class 對象
jclass GetObjectClass(jobject obj)
{ return functions->GetObjectClass(this, obj); }
eg:
Java_com_kpa_jnijavademo_JNIDemo_sayHello(JNIEnv *env, jobject thiz) {
// 根據native sayHello方法中jobject 的class 對象
jclass jniDemo = env->GetObjectClass(thiz);
}
- GetSuperClass
//根據jclass 可以獲取他的父類的jclass 對象
jclass GetSuperclass(jclass clazz)
{ return functions->GetSuperclass(this, clazz); }
4.3 JNI獲取jmethod、jfieldID
GetMethodID/GetStaticMethodID
/**
* clazz 所在class 對象
* name: 方法名稱
* sig: 方法簽名(變量和方法在JNI中都是有簽名的)
**/
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetMethodID(this, clazz, name, sig); }
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetStaticMethodID(this, clazz, name, sig); }
GetFieldID/GetStaticFieldID
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
{ return functions->GetFieldID(this, clazz, name, sig); }
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
{ return functions->GetStaticFieldID(this, clazz, name, sig); }
通過javap命令查看類中的屬性和方法的簽名
javap -s -p XXX.class
eg:
public class com.kpa.jnijavademo.Demo {
public int a;
descriptor: I
public com.kpa.jnijavademo.Demo();
descriptor: ()V //構造方法簽名
public int add(int, int);
descriptor: (II)I
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
}
I、()V、(II)I都是簽名
eg:
void createObj1(JNIEnv *env) {// native 創建Java對象
// 獲取Java中的Date的Class對象
jclass date_clazz = env->FindClass("java/util/Date");
// 獲取構造方法的ID 構造方法名稱始終是<init>
jmethodID mid_date = env->GetMethodID(date_clazz, "<init>", "()V");
// 生成Date對象
jobject date_obj = env->NewObject(date_clazz, mid_date);
jmethodID get_time_id = env->GetMethodID(date_clazz, "getTime", "()J");
jlong time = env->CallLongMethod(date_obj, get_time_id);
printf("時間 ==== %I64d", time);
}
然後看一下上面4.1中NewObject創建一個對象
eg:
// 獲取Java中的Date的Class對象
jclass date_clazz = env->FindClass("java/util/Date");
// 獲取構造方法的ID 構造方法名稱始終是<init>
jmethodID mid_date = env->GetMethodID(date_clazz, "<init>", "()V");
4.4
到此爲止,介紹了JNI中如果獲取對象的Class 對象,創建對象,以及獲取方法、屬性等功能。
在Android NDK特別篇中多介紹幾個使用案例