[轉]Android開發實踐:Java層與Jni層的數組傳遞

[轉]Android開發實踐:Java層與Jni層的數組傳遞
轉自:http://blog.csdn.net/xinchen200/article/details/25333047
Android開發中,經常會在Java代碼與Jni層之間傳遞數組(byte[]),一個典型的應用是Java層把需要發送給客戶端的數據流傳遞到Jni層,由Jni層的Socket代碼發送出去,當然,Jni層也需要把從Socket接收到的數據流返回給Java層。我簡單地總結了一下,從Java層到Jni層,從Jni層到JAVA層,各有3種傳遞方式,下面用代碼示例簡單地介紹一下。

示例代碼的主要文件有兩個,一個是Native.java,是Java層的類;另一個是Native.c,是JNI層的文件,關鍵的地方我都用註釋添加到代碼中了,完整的代碼見博文後面的附件。

一、 從Java傳遞數組到Jni層

Jni層接收到Java層傳遞過來的byte[]數組,一般有2個函數來獲取它的值,一個 GetByteArrayRegion,另一個是 GetByteArrayElements ,前者是進行值拷貝,將Java端數組的數據拷貝到本地的數組中,後者是指針的形式,將本地的數組指針直接指向Java端的數組地址,其實本質上是JVM在堆上分配的這個數組對象上增加一個引用計數,保證垃圾回收的時候不要釋放,從而交給本地的指針使用,使用完畢後指針一定要記得通過ReleaseByteArrayElements進行釋放,否則會產生內存泄露。

首先看Native.java的定義:

再看看對應的native.c的實現代碼:

二、 從Jni層傳遞數組到Java層

把Jni層定義的數組傳遞到Java層,一般有兩種方法,一種是通過native函數的返回值來傳遞,另一種是通過jni層回調java層的函數來傳遞,後者多用於jni的線程中。無論哪種方法,都離不開 SetByteArrayRegion 函數,該函數將本地的數組數據拷貝到了 Java 端的數組中。下面只介紹前一種方式,即通過native函數返回值的方式傳遞jni層的數組,回調的方式其實用法類似,就不詳細介紹了。

首先看Native.java的定義:

再看看native.c是如何實現的:

由上述代碼示例可以看出,首先通過 NewByteArray 在堆上分配數組對象,然後通過SetByteArrayRegion 把本地的數組數據拷貝到堆上分配的數組中去,然後通過返回值將分配的數組對象返回到Java層即可。對於回調的方式,這幾步操作也是一樣的,唯一的不同是,回調方式不是以返回值的方式將數組對象返回給Java層,而是在回調函數中,以回調函數參數的形式返回給Java層。

三、 Direct Buffer 方式傳遞

Java和Jni層的數組傳遞還有一個比較重要的方式,就是通過Direct Buffer來傳遞,這種方式類似於在堆上創建創建了一個Java和Jni層共享的整塊內存區域,無論是Java層或者Jni層均可訪問這塊內存,並且Java端與Jni端同步變化,由於是採用的是共享內存的方式,因此相比於普通的數組傳遞,效率更高,但是由於構造/析構/維護這塊共享內存的代價比較大,所以小數據量的數組建議還是採用上述方式,Direct Buffer方式更適合長期使用頻繁訪問的大塊內存的共享。具體使用方法介紹如下:

首先看Native.java的定義:

再看看native.c是如何實現的:

由上述代碼可以看出,其中使用起來還是很簡單的,Jni層只需要通過GetDirectBufferAddress函數即可獲取到這塊共享的內存的地址,Direct Buffer的管理工作均由操作系統來負責。

四、 總結

關於Java與Jni層的數組傳遞就介紹到這裏了,其實並不複雜,希望上述代碼對初學者能有所幫助,有任何疑問或者不清楚的地方歡迎留言或者來信[email protected]交流。

例子:

//jni 給 java傳遞數組。

static jbyteArray jnicopyjavaArray(JNIEnv *env, unsigned char *data, int len)
{
    jbyteArray array = (*env)->NewByteArray(env, len);//what time release this array.
    if(array==NULL) {
        JNI_DBGPRINT(JNI_DEBUG_ERROR,"-----NewByteArray Failured get null!!!-----");
        return NULL;
    }
    JNI_DBGPRINT(JNI_DEBUG_ERROR,"%s %d array is at %p",__func__,__LINE__,array);
    (*env)->SetByteArrayRegion(env, array, 0, len, data);
    return array;
}
int tellapp_send_over(uint8 *data,int len,int code)
{
    if(gJavaVM == NULL || gJavaObj == NULL){
        JNI_DBGPRINT(JNI_DEBUG_ERROR,"---------------gJavaVM | gJavaObj release---------------");
        return NULL_POINTER;
    }
    JNI_DBGPRINT(JNI_DEBUG_ERROR,"---------------%s len--------=%d-------",__func__,code);

    JNIEnv *env = JNU_GetEnv();
    if(env == NULL){
        JNI_DBGPRINT(JNI_DEBUG_ERROR,"---------------%s env  is null---------------",__func__);
        return ERROR0;
    }

    jclass javaclass = (*env)->GetObjectClass(env, gJavaObj);//find class
    if(javaclass == NULL){
        JNI_DBGPRINT(JNI_DEBUG_ERROR,"---------------%s javaclass  is null---------------",__func__);
        return ERROR0;
    }

    jmethodID javaCallback = (*env)->GetMethodID(env, javaclass, "onSendOver", "([BII)V");
    if(javaCallback != NULL){
        jbyteArray array = jnicopyjavaArray(env, data, len);
        (*env)->CallVoidMethod(env,gJavaObj,javaCallback,array,len,code);//return val?
        (*env)->DeleteLocalRef(env,array);
        (*env)->DeleteLocalRef(env,javaclass);
        return OK;
    }
    else{
        (*env)->DeleteLocalRef(env,javaclass);
        JNI_DBGPRINT(JNI_DEBUG_ERROR,"---------------%s javaCallback  is null---------------",__func__);
        return ERROR0;
    }
}

//java給jni傳遞數組

JNIEXPORT void JNICALL
Java_com_qihoo_smartband_blerpc_Native_setBuffer(JNIEnv *env, jobject instance, jbyteArray data_,
                                                 jint len)
{
    jbyte *pdata = (*env)->GetByteArrayElements(env, data_, NULL);//GetByteArrayRegion
    notifyllc(pdata, len);
    (*env)->ReleaseByteArrayElements(env, data_, pdata, 0);
}//GetByteArrayElements 和 GetByteArrayRegoin的主要作用其實就是在jvm中增加java數組的引用計數而已。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章