Android:JNI之Java和C層的相互調用及多線程的回調實現


一、前言
  Java本機接口(Java Native Interface (JNI))是本機編程接口,它是JDK的一部分,JNI它提供了若干的API,實現了和Java和其他通信(主要是C&C++),用於從Java程序調用C/C++,以及從C/C++程序調用Java代碼。

  本文旨在強化JNI的使用技巧,簡單的使用可另外參考 https://www.cnblogs.com/blogs-of-lxl/p/9268732.htmlJNI接口實現 部分。

 

二、Java層存儲JNI層動態創建的C++對象(Java調用C++)

1.C++層的代碼如下:

 定義了一個食品類,里面有獲取食品名稱和價格的方法。

#pragma once
 
class CFood 
{
private:
    char* name;
    double price;
public:
    CFood(char* name, double price) 
    {
        this->name = name;
        this->price = price;
    }
 
    ~CFood() 
    {
        if(name != NULL) 
        {
            free(name);
            name = NULL;
        }
    }
 
    const char* getName() 
    {
        return this->name;
    }
 
    double getPrice() 
    {
        return this->price;
    }
};

 

2.JNI層的實現代碼:

 通過JNI實現對c++類的調用。

 (1)頭文件:test_Food.h

#include <jni.h>
/* Header for class test_Food */
 
#ifndef _Included_test_Food
#define _Included_test_Food
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test_Food
 * Method:    setFoodParam
 * Signature: (Ljava/lang/String;D)V
 */
JNIEXPORT void JNICALL Java_test_Food_setFoodParam
  (JNIEnv *, jobject, jstring, jdouble);
 
/*
 * Class:     test_Food
 * Method:    getName
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_test_Food_getName
  (JNIEnv *, jobject);
 
/*
 * Class:     test_Food
 * Method:    getPrice
 * Signature: ()D
 */
JNIEXPORT jdouble JNICALL Java_test_Food_getPrice
  (JNIEnv *, jobject);
 
/*
 * Class:     test_Food
 * Method:    finalize
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_test_Food_finalize
  (JNIEnv *, jobject);
 
#ifdef __cplusplus
}
#endif
#endif

 (2)代碼實現:test_Food.cpp

#include "stdafx.h"
#include <stdlib.h>
#include "test_Food.h"
#include "Food.h"
 
// jstring轉string類型方法
char* jstring2string(JNIEnv* env, jstring jstr)
{
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
 
    if (alen > 0)
    {
        rtn = (char*)malloc(alen + 1);
 
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}
 
// char轉jstring類型方法 
jstring char2Jstring(JNIEnv* env, const char* pat)
{
    jclass strClass = env->FindClass("Ljava/lang/String;");
    jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    jbyteArray bytes = env->NewByteArray(strlen(pat));
    env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
    jstring encoding = env->NewStringUTF("utf-8");
    return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}
 
// 給Java層CFood對象指針賦值  
void setFood(JNIEnv *env, jobject thiz, const CFood* pFood) 
{
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID fid = env->GetFieldID(clazz, "mObject", "I");
    env->SetIntField(thiz, fid, (jint)pFood);
}

// 獲取Java層CFood對象指針
CFood* getCFood(JNIEnv *env, jobject thiz) 
{
    jclass clazz = env->GetObjectClass(thiz); //獲取thiz中對象的class對象
    jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); //獲取Java中的mObject字段的id(mObject是Java中存儲C++層對象的指針)
    jint p = env->GetIntField(thiz, fid); //獲取mObject的值
    return (CFood*)p;
}

// JNI接口:供Java調用c++ CFood類創建食品對象
JNIEXPORT void JNICALL Java_test_Food_setFoodParam
  (JNIEnv *env, jobject thiz, jstring name, jdouble price) 
{
    //const char* tempName = env->GetStringUTFChars(name, 0);
    char* tempName = jstring2string(env, name);
    double tempPrice = price;
 
    CFood* pFood = new CFood(tempName, tempPrice); //根據Java層傳下來的參數創建一個食品對象
    setFood(env, thiz, pFood); //將創建的食品對象指針通過JNI賦值給Java層變量
}

// JNI接口:供Java層調用獲取食品名稱
JNIEXPORT jstring JNICALL Java_test_Food_getName
  (JNIEnv *env, jobject thiz)
{
    CFood* pFood = getCFood(env, thiz);
    const char* name = pFood->getName();
    return char2Jstring(env, name);
}
 
// JNI接口:供Java調用獲取食品價格
JNIEXPORT jdouble JNICALL Java_test_Food_getPrice
  (JNIEnv *env, jobject thiz)
{
    CFood* pFood = getCFood(env, thiz);
    return pFood->getPrice();
}

// JNI接口:供Java調用析構c++對象。
JNIEXPORT void JNICALL Java_test_Food_finalize
  (JNIEnv *env, jobject thiz) 
{
    CFood* pFood = getCFood(env, thiz);
    if (pFood != NULL) 
    {
        delete pFood;
        pFood = NULL;
        setFood(env, thiz, pFood);
    }
}

3.Java層為了使用上述代碼,引入一個新的類Food,如下:

public class Food {
 
    static {
        System.loadLibrary("jniFood"); //加載JNI及c++部分代碼編譯生成的so
    }
    
    // 用於存儲C++層的對象指針
    private int mObject;
    
    public Food(String name, double price) {
        setFoodParam(name, price);
    }

  // 本地方法,在JNI實現的接口
  public native void setFoodParam(String name, double price);   public native String getName();   public native double getPrice();   protected native void finalize();

   // 測試   
public static void main(String[] args) { Food f1 = new Food("面包", 1.99); Food f2 = new Food("牛奶", 3.99); System.out.println(String.format("食物:%s, 單價:%f", f1.getName(), f1.getPrice())); System.out.println(String.format("食物:%s, 單價:%f", f2.getName(), f2.getPrice())); }
}

 其中,聲明了本地方法,需要注意的是創建一個int型字段用來存放C++層對象的指針。另外需要注意的是通過本地方法finalize()來析構c++對象。

 

三、C++中存放Java對象C++回調Java

 首先實現單線程的回調,始終將 JNI接口參數中的 JNIEnv * 和 jobject 一起傳參使用,不作保存。

 1.Java層代碼:

package test1;
 
// 內部實現一個MyPrint類  
class MyPrint {
    public void onPrint(String text) {
        System.out.println(text);
    }
}

// MyFile類  
public class MyFile {
 
    private MyPrint myPrint = null;
    
    static {
        System.loadLibrary("jniTest"); //加載JNI動態庫
    }
    
    private int mObject;

  // MyFile類的構造函數 
  public MyFile() {
    init(); //初始化一個C++層的CMyFile對象,並將對象的地址保存到上面的mObject變量中
  }

  // MyFile類中的onPrint方法,調用MyPrint對象的onPrint方法
  public void onPrint(String text) { 
     myPrint.onPrint(text); 
  }   
    
  // 本地保存C++傳過來的myPrint對象
  public void setPrint(MyPrint myPrint) {
    this.myPrint = myPrint;
  }

  // 保存Java本地myPrint方法,同樣在C++的創建CMyprint對象並注冊到CMyFile對象中去
  public void setMyPrint(MyPrint myPrint) {
    setPrint(myPrint);
    this.registerPrint();
  }
    
  public void myPrint(String text) {
    this.doPrint(text);
  }
    
   // 本地方法,在JNI中實現
    public native void init();
    public native void registerPrint(MyPrint myPrint);
    public native void doPrint(String text);
    protected native void finalize();
    
   // 測試
    public static void main(String[] args) {
        MyFile myFile = new MyFile(); //實例化MyFile對象,主要是C++層實例化一個CMyFile對象並將對象指針保存到Java層mObject變量
        MyPrint myPrint = new MyPrint(); //實例化一個Java層MyPrint對象
        
        myFile.setMyPrint(myPrint); //保存本地MyPrint對象,另外C++層也會創建一個CMyPrint對象並注冊到CMyFile對象中去
        myFile.doPrint("hello world!"); /*通過JNI接口調用C++層CMyfile對象中CMyPrint對象的onPrint方法,然后C++層中的onPrint方法又會通過傳過去的 JNIEnv* 和 jobject 來獲取Java對象,並回調Java層的onPrint方法*/
        myFile.doPrint("again!");
        myFile.doPrint("next!");
    }
}

 2.JNI層實現:

  C++頭文件:MyPrint.h

#include "stdafx.h" #include <jni.h> #include <stdlib.h> class CMyPrint { public: jstring char2Jstring(JNIEnv* env, const char* pat) { jclass strClass = env->FindClass("Ljava/lang/String;"); jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V"); jbyteArray bytes = env->NewByteArray(strlen(pat)); env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = env->NewStringUTF("utf-8"); return (jstring)env->NewObject(strClass, ctorID, bytes, encoding); } // 如下傳遞JNIEnv* 和 jobject參數來獲取Java對象,然后回調 void onPrint(JNIEnv *env, jobject thiz, char* text) { jclass clazz = env->GetObjectClass(thiz); jmethodID methodID = env->GetMethodID(clazz, "onPrint", "(Ljava/lang/String;)V"); jstring strText = char2Jstring(env, text); env->CallVoidMethod(thiz, methodID, strText); } };

  C++頭文件:MyFile.h

#pragma once #include "MyPrint.h" class CMyFile { private: CMyPrint* pPrint; public: ~CMyFile() { if (pPrint != NULL) { delete pPrint; pPrint = NULL; } } void registerPrint(CMyPrint* pPrint) { this->pPrint = pPrint; } void doPrint(JNIEnv *env1, jobject thiz, char* text) { pPrint->onPrint(env1, thiz, text); } };

  JNI頭文件:test1_MyFile.h

#include <jni.h>
/* Header for class test1_MyFile */ #ifndef _Included_test1_MyFile #define _Included_test1_MyFile #ifdef __cplusplus extern "C" { #endif /* * Class: test1_MyFile * Method: init * Signature: ()V */ JNIEXPORT void JNICALL Java_test1_MyFile_init (JNIEnv *, jobject); /* * Class: test1_MyFile * Method: registerPrint * Signature: (Ltest1/MyPrint;)V */ JNIEXPORT void JNICALL Java_test1_MyFile_registerPrint (JNIEnv *, jobject); /* * Class: test1_MyFile * Method: doPrint * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_test1_MyFile_doPrint (JNIEnv *, jobject, jstring); /* * Class: test1_MyFile * Method: finalize * Signature: ()V */ JNIEXPORT void JNICALL Java_test1_MyFile_finalize (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif

  JNI實現代碼:

#include "stdafx.h"
#include <jni.h>
#include "MyFile.h"
#include "test1_MyFile.h"
// jstring轉string類型方法 char* jstring2string(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; }
// 獲取Java層mObject保存的指針 CMyFile
* getMyFile(JNIEnv *env, jobject thiz) { jclass clazz = env->GetObjectClass(thiz); jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); jint p = env->GetIntField(thiz, fid); return (CMyFile*)p; }
// 將CMyFile對象指針保存到Java層的mObjecj變量中
void setMyFile(JNIEnv *env, jobject thiz, CMyFile* pFile) { jclass clazz = env->GetObjectClass(thiz); jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); env->SetIntField(thiz, fid, (jint)pFile); }
// 創建C++層的CMyFile對象,並將對象指針保存到Java層 JNIEXPORT
void JNICALL Java_test1_MyFile_init (JNIEnv *env, jobject thiz) { CMyFile* pFile = new CMyFile(); setMyFile(env, thiz, pFile); }
// 創建C++層的CMyPrint對象,並注冊到CMyFile對象中 JNIEXPORT
void JNICALL Java_test1_MyFile_registerPrint (JNIEnv *env, jobject thiz) { CMyPrint* pPrint = new CMyPrint(); CMyFile* pFile = getMyFile(env, thiz); pFile->registerPrint(pPrint); }
// 調用CMyFile對象中注冊的CMyPrint對象的打印方法 JNIEXPORT
void JNICALL Java_test1_MyFile_doPrint (JNIEnv *env, jobject thiz, jstring strText) { CMyFile* pFile = getMyFile(env, thiz); char* pText = jstring2string(env, strText); pFile->doPrint(env, thiz, pText); if (pText != NULL) { free(pText); pText = NULL; } }
// 析構C++層的CMyFile對象 JNIEXPORT
void JNICALL Java_test1_MyFile_finalize (JNIEnv *env, jobject thiz) { CMyFile* pFile = getMyFile(env, thiz); if (pFile != NULL) { delete pFile; pFile = NULL; setMyFile(env, thiz, pFile); } }

 

 上述的回調是在一個線程棧中完成的,那如何實現多線程的回調實現呢?由於JNIEnv *不能被緩存,只在當前線程中有效,而且JNI中接口的參數都是局部引用,當該方法棧執行完畢,局部引用就會被銷毀,所以每次都要獲取JNIEnv *和jobject對象參數進行回調,而可以緩存的是JavaVM*,同樣應該將JavaVM*轉換為全局引用再緩存,jobject也可以轉換為全局引用后緩存。

  繼續在上面代碼進行修改,共同部分就不貼了:
  1. Java層代碼:

package test1;
 
class MyPrint {
    public void onPrint(String text) {
        System.out.println(text);
    }
}
 
public class MyFile {
 
    private MyPrint myPrint = null;
    
    static {
        System.loadLibrary("jniTest");
    }
    
    private int mObject;
 
    public MyFile() {
        init(); //初始化一個C++層的CMyFile對象,並將對象的地址保存到上面的mObject變量中
    }
    
    public void setPrint(MyPrint myPrint) {
        this.myPrint = myPrint;
    }
    
    public void setMyPrint(MyPrint myPrint) {
        setPrint(myPrint);
        this.registerPrint(myPrint); // 保存Java本地myPrint方法,且創建CMyPrint對象保存 JavaVM* , jobject 參數
    }
// 本地方法
public native void init(); protected native void finalize(); public native void registerPrint(MyPrint myPrint); public native void doPrint(String text);
   // 測試
public static void main(String[] args) { MyFile myFile = new MyFile(); MyPrint myPrint = new MyPrint(); myFile.setMyPrint(myPrint); myFile.doPrint("hello world!"); System.out.println("等待打印結果..."); try { Thread.sleep(20*1000); } catch (InterruptedException e) { e.printStackTrace(); } } }

  2.JNI層實現:

CMyFile* getMyFile(JNIEnv *env, jobject thiz) 
{
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID fid = env->GetFieldID(clazz, "mObject", "I");
    jint p = env->GetIntField(thiz, fid);
    return (CMyFile*)p;
}
 
void setMyFile(JNIEnv *env, jobject thiz, CMyFile* pFile)
{
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID fid = env->GetFieldID(clazz, "mObject", "I");
    env->SetIntField(thiz, fid, (jint)pFile);
}
 
JNIEXPORT void JNICALL Java_test1_MyFile_init
  (JNIEnv *env, jobject thiz)
{
    CMyFile* pFile = new CMyFile();
    setMyFile(env, thiz, pFile);
}
 
JNIEXPORT void JNICALL Java_test1_MyFile_finalize
  (JNIEnv *env, jobject thiz)
{
    CMyFile* pFile = getMyFile(env, thiz);
    if (pFile != NULL) 
    {
        delete pFile;
        pFile = NULL;
        setMyFile(env, thiz, pFile);
    }
}
 
JNIEXPORT void JNICALL Java_test1_MyFile_registerPrint
  (JNIEnv *env, jobject thiz, jobject jPrint)
{
    JavaVM* pVM = NULL;
    env->GetJavaVM(&pVM);
 
    // 根據局部引用生成全局引用
    JavaVM* g_pVM = (JavaVM*)env->NewGlobalRef((jobject)pVM);
    jobject g_javaPrint = env->NewGlobalRef(jPrint);
 
    CMyPrint* pPrint = new CMyPrint(g_pVM, g_javaPrint); // 其中會保存 g_pVMg_javaPrint
    CMyFile* pFile = getMyFile(env, thiz);
    pFile->registerPrint(pPrint);
}
 
 
JNIEXPORT void JNICALL Java_test1_MyFile_doPrint
  (JNIEnv *env, jobject thiz, jstring strText)
{
    CMyFile* pFile = getMyFile(env, thiz);
    char* pText = CMyPrint::jstring2string(env, strText);
 
    pFile->doPrint(pText);
 
    if (pText != NULL)
    {
        free(pText);
        pText = NULL;
    }
}

  3.C++層代碼:

typedef struct _ThreadParam
{
    JavaVM* jvm;
    jobject javaPrint;
    string text;
}ThreadParam;
 
DWORD WINAPI funproc(LPVOID lpparentet)  
{  
    Sleep(10*1000);
 
    ThreadParam* param = (ThreadParam*)lpparentet;
 
    JNIEnv* pEnv = NULL;
    param->jvm->AttachCurrentThread((void**)&pEnv, NULL); //附加當前線程到Java(Dalvik)虛擬機(創建CMyPrint時保存了jvm)
 
    jclass clazz = pEnv->GetObjectClass(param->javaPrint);
 
    // 獲取非靜態方法ID
    jmethodID methodID = pEnv->GetMethodID(clazz, "onPrint", "(Ljava/lang/String;)V");
    
    jstring strText = CMyPrint::char2Jstring(pEnv, param->text.c_str());
 
    // 調用非靜態方法
    pEnv->CallVoidMethod(param->javaPrint, methodID, strText);
 
    if (param != NULL) 
    {
        delete param;
        param = NULL;
    }
    return 0;  
} 

/*
 * CMyPrint 類
 */   
class CMyPrint
{
private:
    jobject mJavaPrintObj;
    JavaVM* jvm;
public:
 
    CMyPrint(JavaVM* jvm, jobject javaPrintObj)  // 創建CMyPrint對象時保存JavaVM*和jobject,可用於子線程回調
    {
        this->jvm = jvm;
        this->mJavaPrintObj = javaPrintObj;
    }
 
    ~CMyPrint()
    {
        JNIEnv* pEnv = NULL;
        jvm->AttachCurrentThread((void**)&pEnv, NULL);
        pEnv->DeleteGlobalRef(mJavaPrintObj);
        pEnv->DeleteGlobalRef((jobject)jvm);
    }
 
    static char* jstring2string(JNIEnv* env, jstring jstr)
    {
        char* rtn = NULL;
        jclass clsstring = env->FindClass("java/lang/String");
        jstring strencode = env->NewStringUTF("utf-8");
        jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
        jsize alen = env->GetArrayLength(barr);
        jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
 
        if (alen > 0)
        {
            rtn = (char*)malloc(alen + 1);
 
            memcpy(rtn, ba, alen);
            rtn[alen] = 0;
        }
        env->ReleaseByteArrayElements(barr, ba, 0);
        return rtn;
    }
 
    static jstring char2Jstring(JNIEnv* env, const char* pat)
    {
        jclass strClass = env->FindClass("Ljava/lang/String;");
        jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
        jbyteArray bytes = env->NewByteArray(strlen(pat));
        env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
        jstring encoding = env->NewStringUTF("utf-8");
        return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
    }
 
    void onPrint(char* text) 
    {
        ThreadParam* param = new ThreadParam();
        param->jvm = jvm;
        param->javaPrint = mJavaPrintObj;
        param->text = text;
 
        HANDLE hander = CreateThread(NULL,0,funproc,param,0,NULL); // 創建一個子線程回調Java層的方法
    }
};  

/*
 * CMyFile 類 
 */
class CMyFile 
{
private:
    CMyPrint* pPrint;
public:
 
    ~CMyFile() 
    {
        if (pPrint != NULL) 
        {
            delete pPrint;
            pPrint = NULL;
        }
    }
 
    void registerPrint(CMyPrint* pPrint) 
    {
        this->pPrint = pPrint;
    }
 
    void doPrint(char* text) 
    {
        pPrint->onPrint(text);
    }
};

 

 

、JNI中的字符編碼方式的完美轉換

 1、相關概念:
       (1)、Java層使用的是16bit的unicode編碼(utf-16)來表示字符串,無論中文還是英文,都是兩個字節。
       (2)、JNI層使用的是UTF-8編碼,UTF-8是變長編碼的unicode,一般ascii字符1字節,中文3字節。
       (3)、C/C++使用的是原始數據,ascii就是一個字節,中文一般是GB2312編碼,用兩個字節表示一個漢字。

 2、字符流向
       (1)、Java ---> C/C++ :
         這時候,Java調用的時候使用的是UTF-16編碼,當字符串傳遞給JNI方法時,C/C++得到的輸入是jstring,這時候,JNI提供了兩個函數,一個是GetStringUTFChars,該函數將得到一個UTF-8編碼的字符串(char*類型),另一個函數是GetStringChars,該函數將得到一個UTF-16編碼的字符串(wchar_t*類型)。無論哪種結果,得到的字符串如果含有中文,都需要進一步轉換為GB2312編碼。

       (2)、C/C++ ---> Java :
         這時候,是JNI返回給Java字符串。C/C++首先應該負責把這個字符串變成UTF-8或UTF-16格式,然后通過NewStringUTF或者NewString來把它封裝成jstring,返回給Java就可以了。
    如果字符串中不含中文字符,只是標准的ascii碼,那么使用GetStringUTFChars/NewStringUTF就可以搞定了,因為這種情況下,UTF-8編碼和ascii編碼是一致的,不需要轉換。
    如果字符串中有中文字符,那么在C/C++部分就必須進行編碼轉換。我們需要兩個轉換函數,一個是把UTf-8/-16編碼轉成GB2312;另一個是把GB2312轉成UTF-8/-16。

      這里需要說明一下:linux和win32都支持wchar,這個事實上就是寬度為16bit的unicode編碼UTF-16,所以,如果我們的c/c++程序中完全使用wchar類型,那么理論上就不需要這種轉換。但是實際上,我們不可能完全用wchar來取代char的,所以就目前大多數應用而言,轉換仍然是必須的。

      (3)、使用wide char類型來轉換 :

char* jstringToWindows( JNIEnv *env, jstring jstr )
{ //UTF8/16轉換成gb2312
  int length = (env)->GetStringLength(jstr );
  const jchar* jcstr = (env)->GetStringChars(jstr, 0 );
 
  int clen = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, NULL,0, NULL, NULL );
 
  char* rtn = (char*)malloc( clen ) //更正。作者原來用的是(char*)malloc( length*2+1 ),當java字符串中同時包含漢字和英文字母時,所需緩沖區大小並不是2倍關系。
  int size = 0;
  size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,clen, NULL, NULL );
  if( size <= 0 )
    return NULL;
  (env)->ReleaseStringChars(jstr, jcstr );
  rtn[size] = 0;
  return rtn;
}
 
jstring WindowsTojstring( JNIEnv* env, const char* str )
{//gb2312轉換成utf8/16
    jstring rtn = 0;
    int slen = strlen(str);
    unsigned short * buffer = 0;
    if( slen == 0 )
        rtn = (env)->NewStringUTF(str );
    else
    {
        int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
        buffer = (unsigned short *)malloc( length*2 + 1 );
        if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
            rtn = (env)->NewString(  (jchar*)buffer, length );
    }
    if( buffer )
        free( buffer );
    return rtn;
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM