zoukankan      html  css  js  c++  java
  • 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;
    }

     

  • 相关阅读:
    Android开发之百度地图的简单使用
    Android给ListView添加一个入场动画
    【安卓9】Cursor类、 查询
    【安卓9】SQLiteOpenHelper 类、增删改操作
    【安卓9】SQLiteDatabase类、ContentValues 类
    【安卓9】SQLite数据库
    【安卓8】SD卡操作
    【安卓8】文件的读写
    【安卓8】文件操作
    【安卓7】XML文件解析——PULL解析
  • 原文地址:https://www.cnblogs.com/blogs-of-lxl/p/10622406.html
Copyright © 2011-2022 走看看