NDK之旅必需要知道的一些基本知識

JNIEnv

  • 定義: 至關於一個jni上下文對象。java

  • 做用: 經過JNIEnv的指針可以對Java端的代碼進行操做:編程

    • a.建立Java對象.數組

      jstring str = (env).NewStringUTF("終端研發部");
      jclass jclazz = (
      env).GetObjectClass(obj);緩存

      • b.調用Java對象的方法。bash

        jclz = (*env)->FindClass(env, "java/lang/String");
        jdouble doub = (*env).GetStaticDoubleField()複製代碼
      • c.獲取及設置Java對象的屬性。微信

        jintArray arrayResult = NULL;
        jclass  jclazz = (*env).GetObjectClass(obj);
        jint * elements =(*env).GetIntArrayElements(array,NULL);複製代碼

        Get/Set[Static] Method jvm

        JNI中一般用JType指代Java環境中的類。

typedef _jobject *jobject;  
typedef _jclass *jclass;  
typedef _jthrowable *jthrowable;  
typedef _jstring *jstring;  
typedef _jarray *jarray;  
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 _jobjectArray *jobjectArray;複製代碼

JType都繼承自JObject

[cpp] view plain copy
class _jobject {};  
class _jclass : public _jobject {};  
class _jthrowable : public _jobject {};  
class _jstring : public _jobject {};  
class _jarray : public _jobject {};  
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 _jobjectArray : public _jarray {};複製代碼

jobject的理解

JNIEXPORT void JNICALL Java_com_jue_testnative_TestNative1_hello(JNIEnv *, jobject);

這裏是jobject指代的在Java中調用native方法的java類實例複製代碼

獲取jclass的方法

a. jclass FindClass(const char *name)

b. jclass GetObjectClass(jobject obj)複製代碼

FindClass注意事項:

  • 注意: 會在ClassPath下面尋找類。須要傳入完整類名,注意包與包之間用’/'。函數

    jclass class_str = env->FindClass("java/lang/String");複製代碼

jfiledID/jmethodID的獲取

  • 在natvie方法中獲取/設置字段的值,或者方法調用,須要先獲取相應的field/method的ID工具

    • jfieldID GetFieldID(jclass clazz, const char name, const char sig) ui

    • jmethodID GetMethodID(jclass clazz, const char name, const char sig)

注意sig用來處理函數重載引發的不肯定。

而後經過ID獲取

jobject GetObjectField(jobject obj, jfieldID fieldID)   

jobject CallObjectMethod(jobject obj, jmethodID methodID, ...)  複製代碼

獲取方法的簽名

  • javap -s 命令工具能夠查看一個類的方法的簽名

    javap -s xxx.class

Sin簽名含義細節

類型 相應的簽名
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V
String Ljava/lang/String
Array [Ljava/lang/Object
Method (para s1,para s2) 返回值簽名

jni如何調用Java裏面的方法的

NIEnv提供了一下函函數(可以實現子類對象調用父類方法的功能)

  • Call Method,
  • CallStatic Method,
  • CallNonvirtual Method。

    CallVoidMethod(jobject obj, jmethodID methodID, ...)
    
      void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
    
      void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args)複製代碼

    調用實例方法的三種方法

  • Call Method(jobject obj, jmethodID id, ...);

    boolean funcation(int i, double d, char c) {  
      }  
    
      env->CallBooleanMethod( obj, id_function , 100L, 3.44, L'3');複製代碼
  • Call MethodV(jobject obj, jmethodID id,va_list lst);

  • Call MethodA(jobject obj, jmethodID id, jvalue* v);
    ```
    jvalue是一個聯合體

    [cpp] view plain copy
    typedef union jvalue {

    jboolean z;  
      jbyte    b;  
      jchar    c;  
      jshort   s;  
      jint     i;  
      jlong    j;  
      jfloat   f;  
      jdouble  d;  
      jobject  l;  複製代碼

    } jvalue;

jvalue *args = new jvalue[3];  
args[0].i = 100L;  
args[1].d = 3.44;  
args[2].c = L'3';  
env->CallBooleanMethodA(obj, id_funcation, args);  
delete [] args;      
```複製代碼

Java對象的建立

  • 方式-NewObject

使用NewObject來建立Java對象。

jobject NewObject(jclass clazz, jmethodID methodID, ...)複製代碼

須要先活的相應構造器的name,方法名設定爲 ,另外返回值的簽名是Void

jmethodID GetMethodID(jclass clazz, const char *name, const char *sig)複製代碼

例子:

jclass class_date = env->FindClass("java/util/Date");  
jmethodID method_id = env->GetMethodID(class_date,"<init>","()V");  
jobject now = env->NewObject(class_date, method_id);複製代碼
  • b.方式-AllocObject
    ```
    jclass cls = (env).GetObjectClass(obj);
    jobject oobj = (
    env).AllocObject(cls);
#### Java字符串和C/C++字符串之間的轉換

-  a.在java中字符串String對象是Unicode(UTF-16)碼,每一個字符不管中文仍是英文都佔用兩個字節。

- b.能夠經過JNI接口把Java中的字符串轉換成C/C++中的寬字符串,Java也能夠傳一個UTF-8的字符串(char*) 到C/C++中。

- c.反過來C++能夠經過把一個寬字符串,或者一個UTF-8的字符串來建立一個Java端的對象。

#### memset的注意事項
- C庫函數 void *memset(void *str, int c, size_t n) 複製字符c(unsigned char類型)參數str指向的字符串的前n個字符。

- memset是以字節爲單位,初始化內存塊。
當初始化一個字節單位的數組時,能夠用memset把每一個數組單元初始化成任何你想要的值

    `複製代碼
char data[10];  
memset(data, 1, sizeof(data));    // right  
memset(data, 0, sizeof(data)); 

char str[50];
strcpy(str,"This is string.h library function");
puts(str);
memset(str,'$',7);
puts(str);

```複製代碼
  • 在初始化其餘基礎類型時,則須要注意,好比
int data[10];  
memset(data, 0, sizeof(data));    // right  
memset(data, -1, sizeof(data));    // right  
memset(data, 1, sizeof(data));    // wrong, data[x] would be 0x0101 instead of 1複製代碼

獲取字符串

  • a.取得與某個jstring對象相關的Java字符串
方法 |     做用
---|---
GetStringChars |     取得UTF-16編碼的寬字符串(char*)
GetStringUTFChars |     取得UTF-8編碼的字符串(char*)複製代碼

注意在不使用到的使用,要注意使用ReleaseStringChars,或者ReleaseStringUTFChars釋放拷貝的內存,或Java對象的引用。

還可使用下面的方法,這個方法能夠增長返回jvm中java對象的可能性,可是仍是有可能返回相應java串的拷貝。

這個方法沒有相應的GetStringUTFCritical,因爲Java字符串使用的是UTF-16,要轉換成UTF-8編碼的串原本就須要一次拷貝動做。

數組分爲2種

  • a.基本類型的數組。

  • b.對象類型(Object[])的數組。

    有一個通用於兩種不一樣數組的的函數:獲得數據的長度函數:GetArrayLength

處理基本類型數組

跟處理字符串類似

Get ArrayElements,能夠把Java基本類型的數組轉換成C/C++中的數組,能夠拷貝一份傳本地代碼,也能夠把指向Java中的指針直接傳回本地代碼。

須要用

Release ArrayElements

void ReleaseIntArrayElements(jintArray array,  
                             jint *elems,  
                             jint mode) {  
    functions->ReleaseIntArrayElements(this,array,elems,mode);  
}複製代碼
mode的類型:
  • 0:對Java數組進行更新,並釋放C/C++的數組。

  • JNI_COMMIT: 對Java數組進行更新但不釋放C/C++數組。

  • JNI_ABORT:對Java數組不進行更新,並釋放C/C++數組。

類似GetStringUTFCritical的函數:爲了直接傳回指向Java數組的指針而加入的函數。

void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) {  
    return functions->GetPrimitiveArrayCritical(this,array,isCopy);  
}  
void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) {  
    functions->ReleasePrimitiveArrayCritical(this,array,carray,mode);  
}複製代碼

Get ArrayRegion 類似於GetStringRegion的函數:沒有Release方法,由於咱們是拷貝

void GetIntArrayRegion(jintArray array,  
                       jsize start, jsize len, jint *buf) {  
    functions->GetIntArrayRegion(this,array,start,len,buf);  
}複製代碼

Set ArrayRegion 能夠把指定範圍內的Java數組元素用C++中的元素賦值。

void SetIntArrayRegion(jintArray array, jsize start, jsize len,  
                       const jint *buf) {  
    functions->SetIntArrayRegion(this,array,start,len,buf);  
}複製代碼

建立一個基本類型的Java數組

Array New Array(jsize size),指定長度後返回相應Java基本類型的數組。

處理Object類型數組

JNI中沒有把Java對象類型的數組轉換成C++中的jobject[]的函數。而是直接經過Get/SetObjectArrayElement來對Java的Object數組進行操做。

jobject GetObjectArrayElement(jobjectArray array, jsize index) {  
    return functions->GetObjectArrayElement(this,array,index);  
}  
void SetObjectArrayElement(jobjectArray array, jsize index,  
                           jobject val) {  
    functions->SetObjectArrayElement(this,array,index,val);  
}複製代碼

使用上述方式不須要釋放資源。
能夠根據數組長度和初始值來建立某個類的數組

jobjectArray NewObjectArray(jsize len, jclass clazz,  
                            jobject init) {  
    return functions->NewObjectArray(this,len,clazz,init);  
}複製代碼

java對象在JNI中的引用

在jvm中建立的對象被傳到本地的C/C++代碼的時候,會產生引用。根據jvm的垃圾回收機制,只要引用存在,就不會觸發該引用指向對象的被回收。

這些引用分爲三種

  • 局部引用Local Reference:是最多見的引用,局部引用只在native函數中有效。局部引用的存在會阻止其指向對象的回收。

  • 全局引用Global Reference:

    • 能夠跨越多個線程

    • 在多個navtive方法中有效、

    • 全局引用存在期間會阻止其引用java對象在jvm的回收。

    • 全局引用的建立不是JNI自動建立的。

    • 全局引用的建立要顯示的調用NewGlobalRef函數,而釋放須要調用ReleaseGlobalRef

  • 弱全局引用:

    • 與全局引用類似,建立和釋放都須要由編程人員來進行。

    • 多個線程,多個native方法中有效。

      區別:這個引用不會阻止jvm回收其指向的引用。

      NewWeakGlobalRef與ReleaseWeakGlobalRef來建立和釋放引用。

IsSameObject

IsSameObject在弱全局引用中有一個特別的功能。

把NULL傳給要判斷的object,來判斷弱全局引用指向的java對象是否被回收。

緩存jfieldID,jmethodID.

  • a.經過方法名+簽名來查詢jfieldID,jmethod開銷是很是大的。

  • b.緩存方式:

    • 1.在使用的時候緩存。(Caching at the Point of Use)

      使用static類型的局部變量來保存已經查詢過的ID,這樣就不會在每次調用的時候查詢,而是隻查詢一次。

    • 2.在java類初始化的時候緩存。(Caching at Class's inititalizer)

      這種加載方式在jvm類的加載和從新加載都會從新呼叫該native方法從新計算ID

public class TestNative  
{  
    static {  
        initNativeIDs();  
    }  
    static natvie void initNativeIDs();  
    int propInt = 0;  
}複製代碼
JNIEXPORT void JNICALL    Java_TestNative_initNativeIDs(JNIEnv *env, jobject object)  
{  
    ......  
    g_propInt_id = GetFieldID(clazz,  "propInt", "I" );  

}複製代碼

相信本身,沒有作不到的,只有想不到的

若是你以爲此文對您有所幫助,歡迎入羣 QQ交流羣 :644196190
微信公衆號:終端研發部

技術+職場
技術+職場
相關文章
相關標籤/搜索