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 <android\log.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[]; const jbyte *str; str = (*env)->GetStringUTFChars(env, jstr, NULL); if (str == NULL) { return NULL; /* OutOfMemoryError already thrown */ }
LOGE("123456789:%s\n",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 <android\log.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 > )
{
pStr = (char*)malloc(strLen + ); if (!pStr)
{
return NULL;
} memcpy(pStr, jBuf, strLen); pStr[strLen] = ;
} (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, ); 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\n",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, , 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 <android\log.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 > )
{
pStr = (char*)malloc(strLen + ); if (!pStr)
{
return NULL;
} memcpy(pStr, jBuf, strLen); pStr[strLen] = ;
} (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, ); 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, , 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\n",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, , 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 字符串以’\0’结尾,而 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[]; const jbyte *str; str = (*env)->GetStringChars(env, jstr, NULL); if (str == NULL) { return NULL; /* OutOfMemoryError already thrown */ }
LOGE("123456789:%s\n",str); (*env)->ReleaseStringChars(env, jstr, str); return (*env)->NewStringUTF(env, "填充");
return strToJstring(env,"");
}

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

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[]; const jbyte *str; str = (*env)->GetStringUTFChars(env, jstr, NULL); if (str == NULL) { return NULL; /* OutOfMemoryError already thrown */ }
LOGE("123456789:%s\n",str); (*env)->ReleaseStringUTFChars(env, jstr, str); return (*env)->NewStringUTF(env, "填充");
return strToJstring(env,"");
}

运行的效果就是:

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

成功了。

强调的第二点:

使用了

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

jni 字符串的梳理的更多相关文章

  1. jni 字符串的梳理 2 字符串的处理操作

    我们实现下面的一个功能: 1.首先在java层传递一个字符串到c层,c层首先将jstring转换成char*类型,然后将两个字符串相加,然后再再将char*类型转换成jstring,在上层显示出来 我 ...

  2. Java jni字符串转换

    1.jstring转QString 对于Qt5.2以上(含)可以用QAndroidJniObject::toString(),详见这里:https://stackoverflow.com/questi ...

  3. JNI 引用问题梳理(转)

    局部引用: JNI 函数内部创建的 jobject 对象及其子类( jclass . jstring . jarray 等) 对象都是局部引用,它们在 JNI 函数返回后无效: 一般情况下,我们应该依 ...

  4. 1 通过JNI混合使用Java和C++ -----> 操作字符串

    JNI(Java Native Interface)是Java语言的一部分,可以访问非Java语言编写的程序,也可以用于在C++程序中执行Java代码. 步骤: 1>  编写带有native声明 ...

  5. C++项目通过JNI使用Java第三方jar包

    最近在C++项目中碰到了需要使用第三方公司开发的Java jar包的问题,最后使用了JNI来解决. 参考了网络上不少的方法介绍, 大多数介绍JNI的文章讲的的都是Java通过JNI来调C的本地代码,其 ...

  6. Android 增加JNI

    Android:JNI 与 NDK到底是什么?(含实例教学) 前言 在android开发中,使用NDK开发的需求正逐渐增大: 很多人搞不懂JNI与NDK到底是怎么回事? 今天我们先介绍JNI与NDK之 ...

  7. Android jni/ndk编程二:jni数据类型转换(primitive,String,array)

    一.数据类型映射概述 从我们开始jni编程起,就不可能避开函数的参数与返回值的问题.java语言的数据类型和c/c++有很多不同的地方,所以我们必须考虑当在java层调用c/c++函数时,怎么正确的把 ...

  8. 阿里早期Android加固代码的实现分析

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78320445 看雪上有作者(寒号鸟二代)将阿里移动早期的Android加固进行了逆 ...

  9. JNI/NDK开发指南(四)——字符串处理

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/42066665 从第三章中能够看出JNI中的基本类型和Java中的基本类型都是一一相应的,接 ...

随机推荐

  1. python 03—字符串分割

    字符串分割 例:sentenc = "I am an Englist sentenc" sentence.split() split()把字符串按照空格进行分割,所以得到的结果是 ...

  2. 缓冲区(Buffer)的数据存取

    缓冲区(Buffer) 1. 缓冲区(Buffer):一个用于特定基本数据类 型的容器. 由 java.nio 包定义的,所有缓冲区 都是 Buffer 抽象类的子类.2. Java NIO 中的 B ...

  3. CELF算法原理

    影响力传播模型中的独立层叠模型(independent cascading model,IC模型),影响力传播过程中,种子的影响力具备子模性(submodularity),即种子的边际影响力增量会呈现 ...

  4. GDBT和XGBoost

    https://www.cnblogs.com/pinard/p/6140514.html https://www.cnblogs.com/liuwu265/p/4690486.html https: ...

  5. 泛微 e-cology OA 前台SQL注入

    poc https://github.com/AdministratorGithub/e-cology-OA-SQL 用法:python elog_sql.py http://target 不存在返回 ...

  6. JAVA WEB EL表达式注入

    看猪猪侠以前的洞,顺便总结下: 一.EL表达式简介 EL 全名为Expression Language.EL主要作用: 1.获取数据 EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的we ...

  7. Redis详解(十三)------ Redis布隆过滤器

    本篇博客我们主要介绍如何用Redis实现布隆过滤器,但是在介绍布隆过滤器之前,我们首先介绍一下,为啥要使用布隆过滤器. 1.布隆过滤器使用场景 比如有如下几个需求: ①.原本有10亿个号码,现在又来了 ...

  8. Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树

    一棵10层的二叉树,最多包含多少个结点? 注意当一棵二叉树只有一个结点时为一层. 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余 ...

  9. Java实现 LeetCode 473 火柴拼正方形

    473. 火柴拼正方形 还记得童话<卖火柴的小女孩>吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法.不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到 ...

  10. Java实现 蓝桥杯VIP 算法训练 简单加法

    时间限制:1.0s 内存限制:512.0MB 问题描述 首先给出简单加法算式的定义: 如果有一个算式(i)+(i+1)+(i+2),(i>=0),在计算的过程中,没有任何一个数位出现了进位,则称 ...