NDK開發 - JNI基本數據和字符串處理

第一篇搭建環境,第二篇瞭解開發流程,第三篇扯了一些理論,這一篇是時候展示真正的技術了,主要是寫一些 JNI 的基本類型數據處理以及字符串的處理,字符串是最常常涉及的數據類型。java

傳送門:NDK開發 - JNI基本數據和字符串處理c++

JNI 開發中主要涉及基本數據的處理,引用數據的處理,數組的處理。String 做爲比較經常使用的引用數據類型 JNI 也提供了特有的的方法處理。在處理應用類型的時候須要注意內存的釋放,否則極可能形成內存溢出。C/C++ 中的內存不能依靠 Java 的 GC 線程管理,下一篇會介紹 JNI 的引用類型。
  涉及的代碼地址:https://github.com/gnaix92/as-ndkgit

C 和 C++ 函數實現的比較

  惟一的差別在於用來訪問 JNI 函數的方法。在 C 中,JNI 函數調用由(*env)->做前綴,目的是爲了取出函數指針所引用的值。在 C++ 中,JNIEnv 類擁有處理函數指針查找的內聯成員函數。下面將說明這個細微的差別,其中,這兩行代碼訪問同一函數,但每種語言都有各自的語法。
C 語法: jsize len = (*env)->GetArrayLength(env,array);
C++ 語法: jsize len =env->GetArrayLength(array);github

  下面涉及的代碼都是用 C++ 開發的。因此看到網上用 C 開發的 JNI 也不須要奇怪。編程

處理基本類型

  JNI 中的基本類型和 Java 中的基本類型是一一對應的,這個在上一篇提到過。下面是 JNI 的基本類型定義:數組

typedef unsigned char   jboolean;  
typedef unsigned short  jchar;  
typedef short           jshort;  
typedef float           jfloat;  
typedef double          jdouble;  
typedef int             jint;  
#ifdef _LP64 /* 64-bit Solaris */  
typedef long            jlong;  
#else  
typedef long long       jlong;  
#endif  
typedef signed char     jbyte;

  基本類型很容易理解,就是對 C/C++ 中的基本類型用 typedef 從新定義了一個新的名字,在 JNI 中能夠直接訪問。安全

JNIEXPORT jint JNICALL Java_com_example_gnaix_ndk_NativeMethod_getInt
        (JNIEnv *env, jclass object, jint num)
{
    int len;
    char buf[1024];
    __system_property_get("ro.serialno", buf);
    LOGD("serialno : %s", buf);
    len = strlen(buf);
    return num + len;
}

處理字符串

  JNI 把 Java 中全部對象看成一個 C 指針傳遞到本地方法中,這個指針指向 JVM 中的內部數據結構,而內部的數據結構在內存中的存儲方式是不可見得。只能從 JNIEnv 指針指向的函數表中選擇合適的 JNI 函數來操做 JVM 中的數據結構。
  String 在 Java 是一個引用類型,因此在本地代碼中只能經過GetStringUTFChars 這樣的 JNI 函數來訪問字符串的內容。
先看一個例子:數據結構

JNIEXPORT jstring JNICALL Java_com_example_gnaix_ndk_NativeMethod_getString
        (JNIEnv *env, jclass object, jstring str){

    //1. 將unicode編碼的java字符串轉換成C風格字符串
    const char *buf_name = env->GetStringUTFChars(str, 0);
    if(buf_name == NULL){
        return NULL;
    }
    int len = strlen(buf_name);
    char n_name[len];
    strcpy(n_name, buf_name);

    //2. 釋放內存
    env->ReleaseStringUTFChars(str, buf_name);

    //3. 處理 n_name="ro.serialno"
    char buf[1024];
    __system_property_get(n_name, buf);
    LOGD("serialno : %s", buf);

    //4. 去掉尾部"\0"
    int len_buf = strlen(buf);
    string result(buf, len_buf);

    return env->NewStringUTF(result.c_str());
}

運行結果以下:
4.png函數

訪問字符串

  getString 函數接收一個 jstring 類型的參數 text,可是 jstring 類型是指向 JVM 內部的一個字符串,和 C/C++ 風格的字符串類型 char* 不一樣,因此在 JNI 中不能把 jstring 當作普通的 C/C++ 字符串同樣來使用,必須使用 JNI 的函數來訪問 JVM 內部的字符串數據結構。
const char* GetStringUTFChars(jstring string, jboolean* isCopy)
參數說明:編碼

  • string:Java 傳遞給native代碼的字符串指針

  • isCopy: 取值 JNI_TRUEJNI_FALSE,若是值爲 JNI_TRUE,表示返回 JVM 內部源字符串的一份拷貝,併爲新產生的字符串分配內存空間。若是值爲 JNI_FALSE,表示返回 JVM 內部源字符串的指針,意味着能夠經過指針修改源字符串的內容,不推薦這麼作,由於這樣作就打破了 Java 字符串不能修改的規定。但咱們在開發當中,並不關心這個值是多少,一般狀況下這個參數填 NULL 便可。

  由於 Java 默認使用 Unicode 編碼,而 C/C++ 默認使用 UTF 編碼,因此在本地代碼中操做字符串的時候,必須使用合適的 JNI 函數把 jstring 轉換成 C 風格的字符串。JNI 支持字符串在 Unicode 和 UTF-8 兩種編碼之間轉換,GetStringUTFChars 能夠把一個 jstring 指針(指向 JVM 內部的 Unicode 字符序列)轉換成一個UTF-8 格式的 C 字符串。在上例中 getString 函數中咱們經過 GetStringUTFChars 正確取得了 JVM 內部的字符串內容。

異常檢查

  調用完 GetStringUTFChars 以後不要忘記安全檢查,由於 JVM 須要爲新誕生的字符串分配內存空間,當內存空間不夠分配的時候,會致使調用失敗,失敗後 GetStringUTFChars 會返回 NULL,並拋出一個 OutOfMemoryError 異常。JNI 的異常和 Java 中的異常處理流程是不同的,Java 遇到異常若是沒有捕獲,程序會當即中止運行。而 JNI 遇到未決的異常不會改變程序的運行流程,也就是程序會繼續往下走,這樣後面針對這個字符串的全部操做都是很是危險的,所以,咱們須要用 return 語句跳事後面的代碼,並當即結束當前方法。

釋放字符串

  在調用 GetStringUTFChars 函數從 JVM 內部獲取一個字符串以後,JVM 內部會分配一塊新的內存,用於存儲源字符串的拷貝,以便本地代碼訪問和修改。即然有內存分配,用完以後立刻釋放是一個編程的好習慣。經過調用 ReleaseStringUTFChars 函數通知 JVM 這塊內存已經不使用了,你能夠清除了。注意:這兩個函數是配對使用的,用了 GetXXX 就必須調用 ReleaseXXX,並且這兩個函數的命名也有規律,除了前面的 Get 和 Release 以外,後面的都同樣。

建立字符串

  經過調用 NewStringUTF 函數,會構建一個新的 java.lang.String 字符串對象。這個新建立的字符串會自動轉換成 Java 支持的 Unicode 編碼。若是 JVM 不能爲構造 java.lang.String 分配足夠的內存,NewStringUTF 會拋出一個 OutOfMemoryError 異常,並返回 NULL。在這個例子中咱們沒必要檢查它的返回值,若是 NewStringUTF 建立 java.lang.String 失敗,OutOfMemoryError 這個異常會被在 Java 方法中拋出。若是 NewStringUTF 建立 java.lang.String 成功,則返回一個 JNI 引用,這個引用指向新建立的java.lang.String 對象。

其餘字符串處理函數

  下面這些是在編程中可能會用到的方法。

GetStringChar和ReleaseStringChars

  這對函數和 Get/ReleaseStringUTFChars 函數功能差很少,用於獲取和釋放以 Unicode 格式編碼的字符串。後者是用於獲取和釋放 UTF-8 編碼的字符串。

GetStringLength

  因爲 UTF-8 編碼的字符串以'0'結尾,而 Unicode 字符串不是。若是想獲取一個指向 Unicode 編碼的 jstring 字符串長度,在 JNI 中可經過這個函數獲取。

GetStringUTFLength

  獲取 UTF-8 編碼字符串的長度,也能夠經過標準 C 函數 strlen 獲取。

相關文章
相關標籤/搜索