天天看點

Android NDK開發基礎篇(二)

此系列記錄Android NDK基礎開發知識,在Android NDK開發基礎篇(一)中介紹了NDK、JNI以及關系,包括AS建立JNI的項目及第一個函數解析,

目的

  1. Java類型和native類型的映射關系
  2. jobject
  3. jclass
  4. 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環境,那首先有這幾個概念需要了解一下:

  1. 對象
  2. 屬性
  3. 方法

這是最起碼操作一個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特别篇中多介紹幾個使用案例