Android NDK的C/C++代碼中利用JNI回調實現字元編碼轉換的實驗(中文UTF8與GBK)

來源:互聯網
上載者:User

在NDK下做網路傳輸時,遇到一個編碼轉換的問題,就是對方傳過來的檔案名稱是中文GBK編碼,需要轉成UTF8來處理。

 

平常在C/C++環境下編程時,系統都會提供字元編碼轉換的API。如Windows下有MultiByteToWideChar之類的函數,Linux下有iconv庫,純C下用wcstombs、mbstowcs也可以做。然而我在我機上的Android NDK目錄下,並沒有找到iconv庫,直接寫iconv函數無法編譯;而mbstowcs需要的本地庫似乎也沒有,即使能編譯也會運行失敗。這一來,似乎就沒有辦法,只能自己手工寫碼錶庫轉換了。

 

後來還是想到一個辦法:由於Android下NDK是由Java JNI調用的,而Java本身的字元編碼轉換功能是現成的,直接new一個String,傳入位元組和編碼,就可以擷取指定另一個編碼的字元了。

 

通過網上查資料實驗,終於編譯運行通過,問題解決。Java這邊調JNI的代碼就不貼了,C程式大概內容及說明如下:

 

 

//首先是包含標頭檔,並定義全域變數 #include <string.h>#include <jni.h>#include <pthread.h> //初始JNI虛擬機器環境和線程JavaVM*   gJavaVM;JNIEnv*   gJniEnv;pthread_t gJvmThread; //Java字串的類和擷取位元組的方法IDjclass    gStringClass;jmethodID gmidStringInit;jmethodID gmidStringGetBytes; //初始化JNI環境,此函數由JNI調用jstring Java_com_huz_test_CharsetTest_InitJNIEnv(JNIEnv* env, jobject obj){    (*env)->GetJavaVM(env, &gJavaVM);    gJniEnv=env;    gJvmThread=pthread_self();//記住當前JNI環境的線程     //擷取Java String類和回調方法ID資訊,由於每次轉換都需要,因此用全域變數記下來,免得浪費時間重複執行    gStringClass= (*env)->FindClass(env,"java/lang/String");    gmidStringGetBytes= (*env)->GetMethodID(env,gStringClass, "getBytes", "(Ljava/lang/String;)[B");    gmidStringInit= (*env)->GetMethodID(env,gStringClass, "<init>", "([BLjava/lang/String;)V");     ...     return (*env)->NewStringUTF(env, "OK");} //由Java String轉為指定編碼的charint  jstringToPchar(JNIEnv* env, jstring jstr, const char * encoding, char* outbuf, int outlen){    char* rtn = NULL;    jstring jencoding;    if (encoding==HNULL)        jencoding= (*env)->NewStringUTF(env,"utf-8");    else        jencoding=(*env)->NewStringUTF(env,encoding);    jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, gmidStringGetBytes, jencoding);    jsize alen = (*env)->GetArrayLength(env,barr);    jbyte* ba = (*env)->GetByteArrayElements(env,barr, JNI_FALSE);    if (alen > 0)    {        if(outlen==0)            return alen;        if(outlen<=alen)            return -1;        rtn=outbuf;        memcpy(rtn, ba, alen);        rtn[alen] = 0;    }    (*env)->ReleaseByteArrayElements(env,barr, ba, 0);     return alen;} //由指定編碼以零結束的char轉為Java Stringjstring pcharToJstring(JNIEnv* env, const char* pat, const char* encoding){    jstring jencoding;    jbyteArray bytes = (*env)->NewByteArray(env,strlen(pat));    (*env)->SetByteArrayRegion(env,bytes, 0, strlen(pat), (jbyte*)pat);    if (encoding==HNULL)        jencoding= (*env)->NewStringUTF(env,"utf-8");    else        jencoding=(*env)->NewStringUTF(env,encoding);     return (jstring)(*env)->NewObject(env,gStringClass, gmidStringInit, bytes, jencoding);} //在C代碼中執行字串編碼轉換//參數分別為:原始字元,原始編碼,目標字元緩衝區,目標編碼,目標緩衝區大小,返加轉換結果的長度int changeCharset(char * src_buf, char * src_encoding, char * dst_buf, char * dst_encoding, int dst_size){    JNIEnv *env;    jstring jtemp;    int res;     //由於初始化只執行了一次,本函數與初始JNI調用可能不在同一線程,因此需要判斷當前線程    if(gJvmThread==pthread_self())    {        //如果是同一個線程,直接轉        env=gJniEnv;        jtemp=pcharToJstring(env, src_buf, src_encoding);        res=jstringToPchar(env, jtemp,dst_encoding, dst_buf, dst_size);    }    else    {        //如果不是同一個線程,先Attach再轉        env=gJniEnv;        (*gJavaVM)->AttachCurrentThread(gJavaVM,&env,NULL);        jtemp=pcharToJstring(env, src_buf, src_encoding);        res=jstringToPchar(env, jtemp,dst_encoding, dst_buf, dst_size);        (*gJavaVM)->DetachCurrentThread(gJavaVM);    }    return res;} 

 

雖然JNI回調能解決此問題,但用起來很麻煩,速度上估計也很慢,適合少量的資料處理。估計以後Android的NDK新版應該會解決此問題。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.