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. Java IO(三)FileDescriptor

    Java IO(三)FileDescriptor 一.介绍 FileDescriptor 是文件描述符,用来表示开放文件.开放套接字等.当 FileDescriptor 表示文件时,我们可以通俗的将 ...

  2. 解决google play上架App设置隐私政策声明问题

    在我们的app上架到google play后,为了赚点小钱,就集成google ads,然而这会引发一个新的问题,那就是设置隐私政策声明的问题,通常我们会收到一封来自google play的邮件,提示 ...

  3. parrot os vm镜像failed to fetch cdrom apt-get update的问题

    vi /etc/apt/sources.list 注释掉第一行 cdrom x保存就可以了

  4. HTML5+CSS+JS 贪吃蛇demo

    我写博客的主要目的就是温习所学的知识,自己以前写的游戏当然不能放过! 这款网页版贪吃蛇是大一下册学习网页前端时老师教我们写的,由于那个时候初学网页前端,所以这款游戏是纯原生Java Script写的, ...

  5. DOM、事件面试题

    1.DOM 事件级别有哪些? DOM0 a. 通过定义执行函数 element.onclick = function (){ } b. 直接在标签中添加执行语句 Onclick = '语句' 优点:很 ...

  6. 从0开始探究vue-公共变量的管理

    背景 在Vue项目中,我们总会遇到一些公共数据的处理,如方法拦截,全局变量等,本文旨在解决这些问题 解决方案 事件总线 所谓事件总线,就是在当前的Vue实例之外,再创建一个Vue实例来专门进行变量传递 ...

  7. css背景图定位和浮动

    网站图标引入:<link rel="shortcut icon" href="ico图标地址"> 背景图片  background-image: u ...

  8. Sched_Boost小结

    之前遇到一个耗电问题,最后发现是/proc/sys/kernel/sched_boost节点设置异常,一直处于boost状态.导致所有场景功耗上升. 现在总结一下sched_boost的相关知识. S ...

  9. Java实现 蓝桥杯 算法提高 计算超阶乘(暴力)

    试题 算法提高 计算超阶乘 问题描述 计算1*(1+k)(1+2k)(1+3k)-(1+n*k-k)的末尾有多少个0,最后一位非0位是多少. 输入格式 输入的第一行包含两个整数n, k. 输出格式 输 ...

  10. Java实现 LeetCode 671 二叉树中第二小的节点(遍历树)

    671. 二叉树中第二小的节点 给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0.如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值. 给出这样的 ...