zoukankan      html  css  js  c++  java
  • jni 字符串的梳理

    1、实现的功能是java层传递一个字符串到c层
    2、c层首先将jstring类型转换成char*类型
    3、c层对字符串进行处理之后,将处理之后的char*类型转换成jstring类型返回给上层的

    package
    im.weiyuan.com.jni; public class Sdk { static { System.loadLibrary("hello"); } public Sdk() { } //单例 private static class SdkHodler { static Sdk instance = new Sdk(); } public static Sdk getInstance() { return SdkHodler.instance; } //创建一个字符串到c层,然后c层将字符串拼接成功之后回调到上层显示出来,主要字符串中存在中文乱码显示的问题 public native String getStringFromC(String str); }

    我们来看底层native层c的代码:

    //
    // Created by wei.yuan on 2017/6/13.
    //
    #include <jni.h>
    #include <string.h>
    #include <pthread.h>
    #include "im_weiyuan_com_jni_Sdk.h"
    #include "im_weiyuan_com_jni_Sdk_OnSubProgressListener.h"
    #include "im_weiyuan_com_jni_Sdk_SdkHodler.h"
    JavaVM *g_VM;
    jobject g_obj;
    #include <jni.h>
    #include <string.h>
    #include <time.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dlfcn.h>
    #include <assert.h>
    #include <androidlog.h>
    #include <errno.h>
    #include <pthread.h>
    #include <android/log.h>
    
    
    #define LOG_TAG "Native"
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    
    /*1、第一步将输入的jstring类型转换成char*类型
     * 2、将两个char*类型的字符串拼接起来
     * 3、将cha*类型的转换成jstring类型,返回给java层
     *
     *
     *
     *
     */
    JNIEXPORT jstring JNICALL Java_im_weiyuan_com_jni_Sdk_getStringFromC
            (JNIEnv * env, jobject jobj, jstring jstr){
    //创建字符串,用给定的C字符串创建Java字符串
    char buf[128];
    
    const jbyte *str;
    
    str = (*env)->GetStringUTFChars(env, jstr, NULL);
    
    if (str == NULL) {
    
    return NULL; /* OutOfMemoryError already thrown */
    
    }
    LOGE("123456789:%s
    ",str);
    
    (*env)->ReleaseStringUTFChars(env, jstr, str);
    
    return (*env)->NewStringUTF(env, "填充");
    }

    我们来来看上层activity代码的调用:

    package im.weiyuan.com.jni;
    
    import android.app.Activity;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //调用native的方法
    Toast.makeText(this,""+Sdk.getInstance().getStringFromC("中国无论我的数据节点是就1555556"),Toast.LENGTH_LONG).show();
     } }

    我们上层传入的字符串是数子和英文的组合的时候:我们来看底层的打印日志

    06-22 15:57:15.433 5863-5863/? E/Native: 123456789:中国无论我的数据节点是就1555556

    现在我们把上传的代码修改为:
    package im.weiyuan.com.jni;
    
    import android.app.Activity;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.Toast;
    
    import java.io.UnsupportedEncodingException;
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //调用native的方法
            String str = null;
            try {
                str = new String("中国无论我的数据节点是就1555556".getBytes(),"GB2312");
                Toast.makeText(this,""+Sdk.getInstance().getStringFromC(str),Toast.LENGTH_LONG).show();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    

    我们来看下底层的打印输出:

    06-22 16:02:38.457 11639-11639/? E/Native: 123456789:涓�浗鏃犺�鎴戠殑鏁版嵁鑺傜偣鏄�氨1555556
    06-22 16:02:57.612 12496-12496/? E/Native: 123456789:涓�浗鏃犺�鎴戠殑鏁版嵁鑺傜偣鏄�氨1555556

    乱码如何解决该问题了:

    使用下面的函数

    在使用jni调用时经常遇到数据类型转换问题,以下是char*与jstring相互转换的代码:
    
    //java字符串转C字符串
    char* jstringTostr(JNIEnv* env, jstring jstr)
    {        
        char* pStr = NULL;
    
        jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");
        jstring    encode    = (*env)->NewStringUTF(env, "GB2312");
        jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, encode);
        jsize      strLen    = (*env)->GetArrayLength(env, byteArray);
        jbyte      *jBuf     = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);
    
        if (jBuf > 0)
        {
            pStr = (char*)malloc(strLen + 1);
    
            if (!pStr)
            {
                return NULL;
            }
    
            memcpy(pStr, jBuf, strLen);
    
            pStr[strLen] = 0;
        }
    
        (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, 0);
    
        return pStr;
    }
    //
    // Created by wei.yuan on 2017/6/13.
    //
    #include <jni.h>
    #include <string.h>
    #include <pthread.h>
    #include "im_weiyuan_com_jni_Sdk.h"
    #include "im_weiyuan_com_jni_Sdk_OnSubProgressListener.h"
    #include "im_weiyuan_com_jni_Sdk_SdkHodler.h"
    JavaVM *g_VM;
    jobject g_obj;
    #include <jni.h>
    #include <string.h>
    #include <time.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dlfcn.h>
    #include <assert.h>
    #include <androidlog.h>
    #include <errno.h>
    #include <pthread.h>
    #include <android/log.h>
    
    
    #define LOG_TAG "Native"
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    
    //java字符串转C字符串
    char* jstringTostr(JNIEnv* env, jstring jstr)
    {
        char* pStr = NULL;
    
        jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");
        jstring    encode    = (*env)->NewStringUTF(env, "GB2312");
        jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, encode);
        jsize      strLen    = (*env)->GetArrayLength(env, byteArray);
        jbyte      *jBuf     = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);
    
        if (jBuf > 0)
        {
            pStr = (char*)malloc(strLen + 1);
    
            if (!pStr)
            {
                return NULL;
            }
    
            memcpy(pStr, jBuf, strLen);
    
            pStr[strLen] = 0;
        }
    
        (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, 0);
    
        return pStr;
    }
    /*1、第一步将输入的jstring类型转换成char*类型
     * 2、将两个char*类型的字符串拼接起来
     * 3、将cha*类型的转换成jstring类型,返回给java层
     *
     *
     *
     *
     */
    JNIEXPORT jstring JNICALL Java_im_weiyuan_com_jni_Sdk_getStringFromC
            (JNIEnv * env, jobject jobj, jstring jstr){
    //创建字符串,用给定的C字符串创建Java字符串
    
    const jbyte *str;
    
    str = jstringTostr(env,jstr);
    
    LOGE("123456789:%s
    ",str);
    
    
    return (*env)->NewStringUTF(env, "填充23532");
    }
    我们来运行看下程序的代码:
    06-22 16:10:29.702 21027-21027/im.weiyuan.com.jni E/Native: 123456789:中国无论我的数据节点是就1555556
    解决了该问题,需要注意的是:

     str = new String("中国无论我的数据节点是就1555556".getBytes(),"GB2312");上层采用的是
    GB2312的编码的方式,底层的代码就要采用:

       jstring    encode    = (*env)->NewStringUTF(env, "GB2312");这里就要写成GB2312的格式


    通过这里也具有同样的工具类:
    //C字符串转java字符串
    jstring strToJstring(JNIEnv* env, const char* pStr)
    {
        int        strLen    = strlen(pStr);
        jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");
        jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "<init>", "([BLjava/lang/String;)V");
        jbyteArray byteArray = (*env)->NewByteArray(env, strLen);
        jstring    encode    = (*env)->NewStringUTF(env, "utf-8");
    
        (*env)->SetByteArrayRegion(env, byteArray, 0, strLen, (jbyte*)pStr);
        
        return (jstring)(*env)->NewObject(env, jstrObj, methodId, byteArray, encode);
    }

    将char*类型转换成jstring类型,编码方式是utf-8的编码方式

    (*env)->NewStringUTF(env, "utf-8");

    我们来测试下
    //
    // Created by wei.yuan on 2017/6/13.
    //
    #include <jni.h>
    #include <string.h>
    #include <pthread.h>
    #include "im_weiyuan_com_jni_Sdk.h"
    #include "im_weiyuan_com_jni_Sdk_OnSubProgressListener.h"
    #include "im_weiyuan_com_jni_Sdk_SdkHodler.h"
    JavaVM *g_VM;
    jobject g_obj;
    #include <jni.h>
    #include <string.h>
    #include <time.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dlfcn.h>
    #include <assert.h>
    #include <androidlog.h>
    #include <errno.h>
    #include <pthread.h>
    #include <android/log.h>
    
    
    #define LOG_TAG "Native"
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    
    //java字符串转C字符串
    char* jstringTostr(JNIEnv* env, jstring jstr)
    {
        char* pStr = NULL;
    
        jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");
        jstring    encode    = (*env)->NewStringUTF(env, "GB2312");
        jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, encode);
        jsize      strLen    = (*env)->GetArrayLength(env, byteArray);
        jbyte      *jBuf     = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);
    
        if (jBuf > 0)
        {
            pStr = (char*)malloc(strLen + 1);
    
            if (!pStr)
            {
                return NULL;
            }
    
            memcpy(pStr, jBuf, strLen);
    
            pStr[strLen] = 0;
        }
    
        (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, 0);
    
        return pStr;
    }
    
    
    //C字符串转java字符串
    jstring strToJstring(JNIEnv* env, const char* pStr)
    {
        int        strLen    = strlen(pStr);
        jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");
        jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "<init>", "([BLjava/lang/String;)V");
        jbyteArray byteArray = (*env)->NewByteArray(env, strLen);
        jstring    encode    = (*env)->NewStringUTF(env, "utf-8");
    
        (*env)->SetByteArrayRegion(env, byteArray, 0, strLen, (jbyte*)pStr);
    
        return (jstring)(*env)->NewObject(env, jstrObj, methodId, byteArray, encode);
    }
    /*1、第一步将输入的jstring类型转换成char*类型
     * 2、将两个char*类型的字符串拼接起来
     * 3、将cha*类型的转换成jstring类型,返回给java层
     *
     *
     *
     *
     */
    JNIEXPORT jstring JNICALL Java_im_weiyuan_com_jni_Sdk_getStringFromC
            (JNIEnv * env, jobject jobj, jstring jstr){
    //创建字符串,用给定的C字符串创建Java字符串
    
    const jbyte *str;
    
    str = jstringTostr(env,jstr);
    
    LOGE("123456789:%s
    ",str);
    
    
    return strToJstring(env,"接口开减肥啊数据库");
    }

    我们返回的数据是

    接口开减肥啊数据库,采用的是utf-8格式的编码:
    我们来看下界面的显示:
    界面显示的正确
    我们修改编码方式为gb2312
    我们来看下程序的代码:
    //C字符串转java字符串
    jstring strToJstring(JNIEnv* env, const char* pStr)
    {
        int        strLen    = strlen(pStr);
        jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");
        jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "<init>", "([BLjava/lang/String;)V");
        jbyteArray byteArray = (*env)->NewByteArray(env, strLen);
        jstring    encode    = (*env)->NewStringUTF(env, "gb2312");
    
        (*env)->SetByteArrayRegion(env, byteArray, 0, strLen, (jbyte*)pStr);
    
        return (jstring)(*env)->NewObject(env, jstrObj, methodId, byteArray, encode);
    }

    我们运行这个时候界面显示全是乱码,因为界面默认采用utf-8的编码方式,我们使用gb2132编码

    我们来看程序的代码修改成下面的形式是否可以解决该问题

    package im.weiyuan.com.jni;
    
    import android.app.Activity;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.Toast;
    
    import java.io.UnsupportedEncodingException;
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //调用native的方法
            String str = null;
            try {
                str = new String("中国无论我的数据节点是就1555556".getBytes(),"GB2312");
                byte[] result = Sdk.getInstance().getStringFromC(str).getBytes();
                String aa = new String(result,"GB2312");
                Toast.makeText(this,""+aa,Toast.LENGTH_LONG).show();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
        }
    }

    运行的时候界面还是提示乱码,说明上面的方式只能指定编码方式是utf-8的形式

    NewStringUTF(env, "utf-8");

    jni的内部实现中是用UTF8作为字符串编码格式的,所以使用UTF8系列比较合适

    GetStringUTFChars将jstring转换成为UTF-8格式的char*
    GetStringChars将jstring转换成为Unicode格式的char*
    ReleaseStringUTFChars释放指向UTF-8格式的char*的指针
    ReleaseStringChars释放指向Unicode格式的char*的指针
    NewStringUTF创建一个UTF-8格式的String对象
    NewString创建一个Unicode格式的String对象
    GetStringUTFLengt获取 UTF-8格式的char*的长度 
    GetStringLength获取Unicode格式的char*的长 度

    (1)GetStringUTFChars可以把一个 jstring指针(指向JVM内部的Unicode字符序列)转化成一个UTF-8格式的C 字符串。
    (2)从GetStringUTFChars 中获取的UTF-8字符串在本地代码中使用完毕后,要使用ReleaseStringUTFChars 告诉 JVM 这个 UTF-8 字符串不会被使用了,因为这个UTF-8字符串占用的内存会被回收。 
    (3)JNI 函数 NewStringUTF 在本地方法中创建一个新的java.lang.String字符串对象.这个新创建的字符串对象拥有一个与给定的
    UTF-8编码的C类型字符串内容相同的 Unicode 编码字符串

    UTF-8 字符串以’’结尾,而 Unicode 字符串不是。
    如果一个jstring指向一个 UTF-8编码的字符串,为了得到这个字符串的字节长度,可以调用标准 C 函数 strlen,当然也可以用GetStringUTFLength

    GetStringChars 和 GetStringUTFChars 函数中的第三个参数需要更进一步的解释: 
    const jchar * GetStringChars(JNIEnv *env, jstring str, jboolean *isCopy);

    所以特别强调:

    将jstring转换成char*类型的字符串,最后转换成UTF-8格式的,因为UTF-8格式可以使用标准的<string.h>对应的库函数,例如strlen,strcat等函数,使用GetStringChars获得是一个Unicode的编码

    我们来看下程序的代码和运行效果:

    Toast.makeText(this,""+Sdk.getInstance().getStringFromC("在接口就开始哭的开机 "),Toast.LENGTH_LONG).show();
    底层的代码为:
    JNIEXPORT jstring JNICALL Java_im_weiyuan_com_jni_Sdk_getStringFromC
            (JNIEnv * env, jobject jobj, jstring jstr){
    //创建字符串,用给定的C字符串创建Java字符串
        char buf[128];
    
        const jbyte *str;
    
        str = (*env)->GetStringChars(env, jstr, NULL);
    
        if (str == NULL) {
    
            return NULL; /* OutOfMemoryError already thrown */
    
        }
        LOGE("123456789:%s
    ",str);
    
        (*env)->ReleaseStringChars(env, jstr, str);
    
        return (*env)->NewStringUTF(env, "填充");
    return strToJstring(env,"1234565566555");
    }

    我们来看下程序运行的效果是:

    06-22 16:53:47.082 13194-13194/im.weiyuan.com.jni E/Native: 123456789:(W�c�S1
    06-22 16:55:09.698 15416-15416/im.weiyuan.com.jni E/Native: 123456789:(W�c�S1
    06-22 16:56:10.397 17258-17258/? E/Native: 123456789:(W�c�S1

    是乱码:

    改为:

    JNIEXPORT jstring JNICALL Java_im_weiyuan_com_jni_Sdk_getStringFromC
            (JNIEnv * env, jobject jobj, jstring jstr){
    //创建字符串,用给定的C字符串创建Java字符串
        char buf[128];
    
        const jbyte *str;
    
        str = (*env)->GetStringUTFChars(env, jstr, NULL);
    
        if (str == NULL) {
    
            return NULL; /* OutOfMemoryError already thrown */
    
        }
        LOGE("123456789:%s
    ",str);
    
        (*env)->ReleaseStringUTFChars(env, jstr, str);
    
        return (*env)->NewStringUTF(env, "填充");
    return strToJstring(env,"1234565566555");
    }

    运行的效果就是:

    06-22 16:56:40.117 17923-17923/im.weiyuan.com.jni E/Native: 123456789:在接口就开始哭的开机 

    成功了。

    强调的第二点:

    使用了

    GetStringUTFChars一定要记得使用
    ReleaseStringUTFChars释放,否则会产生内存泄露。

     
     
  • 相关阅读:
    使用gRPC搭建Server端与Client端
    Protocol Buffers 介绍
    测试指标
    linux命令 host-常用的分析域名查询工具
    hping3
    linux命令 iperf-网络性能测试工具
    linux命令 dig-域名查询工具
    linux arping-通过发送ARP协议报文测试网络
    linux nslookup-查询域名DNS信息的工具
    linux netstat-查看Linux中网络系统状态信息
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/7065968.html
Copyright © 2011-2022 走看看