Android Studio NDK 入门教程(2)--Java与C++之间的简单数据转换与传递

概述

本文将讲解Java与原生代码之间的数据转换,包括基础类型之间的转换,以及数组的传递与转换。

类型转换表

JAVA基础类型与C++之间的对应表

Java类型 C/C++类型 描述
boolean jboolean 无符号8位整数
byte jbyte 无符号8位整数
char jchar 有符号16位整数
short jshort 有符号16位整数
int jint 有符号32位整数
long jlong 有符号64位整数
float jfloat 32位单精度浮点数
double jdouble 64位双精度浮点数

这一点可以从jni.h头文件中定义看到:

typedef unsigned char   jboolean;       /* unsigned 8 bits */
typedef signed char     jbyte;          /* signed 8 bits */
typedef unsigned short  jchar;          /* unsigned 16 bits */
typedef short           jshort;         /* signed 16 bits */
typedef int             jint;           /* signed 32 bits */
typedef long long       jlong;          /* signed 64 bits */
typedef float           jfloat;         /* 32-bit IEEE 754 */
typedef double          jdouble;        /* 64-bit IEEE 754 */

由上面的JNI定义代码可以看到,基础类型都是根据Java类型大小等价转换的。因此在使用Java传递过来的基础类型或者返回Java中的基础类型都可以直接使用。比如:

JNIEXPORT jint JNICALL
Java_com_example_wastrel_test_Test_BaseTypeTest(JNIEnv *env, jclass type,jint i) {
    return i+5;
}

Java引用类型与C++之间的对应表

Java类型 C/C++类型 描述
Objec jobject 任何Java对象
Class jclass Class类对象
String jstring String类对象
Object[] jobjectArray 对象数组
boolean[] jbooleanArray 布尔数组
byte[] jbyteArray 字节数组
char[] jcharArray 字符型数组
short[] jshortArray 短整型数组
int[] jintArray 整型数组
long[] jlongArray 长整型数组
float[] jfloatArray 浮点型数组
double[] jdoubleArray 双精度浮点型数组
/*
 * Reference types, in C++
 */
class _jobject {};
class _jclass : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jobjectArray : public _jarray {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jthrowable : public _jobject {};

typedef _jobject*       jobject;
typedef _jclass*        jclass;
typedef _jstring*       jstring;
typedef _jarray*        jarray;
typedef _jobjectArray*  jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray*    jbyteArray;
typedef _jcharArray*    jcharArray;
typedef _jshortArray*   jshortArray;
typedef _jintArray*     jintArray;
typedef _jlongArray*    jlongArray;
typedef _jfloatArray*   jfloatArray;
typedef _jdoubleArray*  jdoubleArray;
typedef _jthrowable*    jthrowable;
typedef _jobject*       jweak;

通过jni里的定义可以看到,任何引用类型(数组在Java中也是引用传递的)传递到C++里面只是一个指向Java对象的指针。因此引用类型不能直接使用,JNIEnv提供了大量的方法来完成了他们之间的转换。

使用JNIEnv完成数据之间的转换

这里只说明引用对象之间的转换,因为对于普通类型,Java与C++是互通的。

String的传递

这里将String单独拿出来讲解,因为String在Java中有着超高的使用频次,JNIEnv对String有相对应的转换函数。JNI里的函数定义如下:

/**转换成unicode相关函数 */
jstring     NewString(const jchar*, jsize);
jsize       GetStringLength(jstring);
const jchar* GetStringChars(jstring, jboolean* isCopy);
void        ReleaseStringChars(jstring, const jchar*);
/** 转换成UTF-8相关函数 */
jstring     NewStringUTF(const char*);
jsize       GetStringUTFLength(jstring);
const char* GetStringUTFChars(jstring, jboolean* isCopy);
void        ReleaseStringUTFChars(jstring, const char*);

当从 JNI 函数 GetStringChars中返回得到字符串B时,如果B是原始字符串java.lang.String 的拷贝,则isCopy被赋值为JNI_TRUE。如果B和原始字符串指向的是JVM中的同一份数据,则 isCopy被赋值为 JNI_FALSE。

当isCopy值为JNI_FALSE时,本地代码决不能修改字符串的内容,否则JVM中的原始字符串也会被修改,这会打破 JAVA语言中字符串不可变的规则。

通常,因为你不必关心JVM是否会返回原始字符串的拷贝,你只需要为 isCopy传递NULL作为参数。

由以上代码,我们可以清晰的看到String处理函数分为了Unicode和UTF-8两种编码类型,但在Android中使用的UTF-8相关的函数,两种编码提供的功能都基本一致,从函数名就可以看出对应函数的功能。下面通过一个使用C++合并String的例子来演示如何使用:

//Java中定义Native函数
 public native static String strConcat(String str1,String str2);
//生成C++函数并用C++实现字符串连接功能
#include "string.h"
JNIEXPORT jstring JNICALL Java_com_example_wastrel_test_Test_strConcat
        (JNIEnv *env, jclass clazz, jstring str1, jstring str2){
        //将jstring转换成const char*指针,使用const修饰符表示其内容不可被修改
        const char* c1=env->GetStringUTFChars(str1, NULL);
        const char* c2=env->GetStringUTFChars(str2, NULL);
        //计算新字符串的长度
        int size=strlen(c1)+strlen(c2);
        //创建一个新的字符串,这里长度+1是为了使字符串有结尾标记‘\0‘
        char * n_char=new char[size+1];
        //利用C标准库提供的字符串操作方法对字符串进行连接,这里需要include"string.h"头文件
        strcpy(n_char,c1);
        strcat(n_char,c2);
        //将生成的新字符串转换成UTF的jstring
        jstring rs=env->NewStringUTF(n_char);
        //删除刚刚分配的内存 避免引起内存泄漏
        delete [] n_char;
        //通知JVM虚拟机Native代码不在持有字符串的引用,说明白点,就是告诉虚拟机我不使用它了,你可以回收了。
        //因为在JVM中如果对象被引用,那么对象将不会被回收。
        //这里为什么要传递jstring和生成的char*呢?是因为char*有可能是jstring的拷贝,如果是拷贝,那么char*就应该被删除。
        env->ReleaseStringUTFChars(str1,c1);
        env->ReleaseStringUTFChars(str2,c2);
        return rs;
}
//然后我们在Java中调用该函数
print(Test.strConcat("里约奥运",",中国加油"));

注:本处以及往后代码中使用的print函数仅仅是把结果追加显示在界面上的TextView上代码如下:

private void print(String str)
{
    tv.append(str+"\n");
}

生成C函数过程请参照上一篇文章:http://blog.csdn.net/venusic/article/details/52121254/

运行结果:

基础数据的数组传递

数组传递跟字符串传递一样,Native收到的都是引用的形式,因此JNIEnv也提供了一些列的方法来完成数据的转换。因为不同数据类型之间的调用方式基本一致,此处使用int型数组作为讲解,int[]传递到Native后收到的是jintArray对象。

//获得数组的长度,该方法适用于所有jarray对象
jsize GetArrayLength(jarray array)

//在本地创建一个jint数组,这个数组只能通过SetIntArrayRegion赋值。
jintArray NewIntArray(jsize length);
//将jintArray转换成jint指针
jint* GetIntArrayElements(jintArray array, jboolean* isCopy);
//取出数组中的部分元素放在buf里
void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf);
//给jintArray按区间赋值
void SetIntArrayRegion(jintArray array, jsize start, jsize len,const jint* buf);
//释放jntArray,第一个参数表示传过来的jintArray,第二参数表示获取到的本地数组指针
//第三个参数需要重点说明,该参数有三个取值:0、JNI_COMMIT、JNI_ABORT
//取值 零(0) 时,更新数组并释放所有元素;
//取值 JNI_COMMIT 时,更新但不释放所有元素;
//取值 JNI_ABORT 时,不作更新但释放所有元素;
//一般实际应用中取0较多
void ReleaseIntArrayElements(jintArray array, jint* elems,jint mode);

例子1:来自JNI的int数组

//Java中定义Native函数,size表示返回数组的大小
public native static int[] getIntArray(int size);
//生成Native函数并实现方法
#include "stdlib.h"
#include "time.h"
//定义随机数产生宏 表示产生0~x之间的随机数
#define random(x) (rand()%x)
JNIEXPORT jintArray JNICALL Java_com_example_wastrel_test_Test_getIntArray
        (JNIEnv *env, jclass clazz, jint size){

        //用时间变量初始化随机数产生器
        srand((int)time(0));
        jint* rs=new jint[size];
        for (int i=0;i<size;i++)
        {
            //调用宏产生0~100的随机数
            rs[i]=random(100);
        }
        //通过JNIEnv的NewIntArray方法new一个jintArray对象
        jintArray array=env->NewIntArray(size);
        //把产生的随机数值赋值给jintArray
        env->SetIntArrayRegion(array,0,size,rs);
        return array;
}
//Java中调用函数
int []rs=Test.getIntArray(10);
print("来自于JNI的Int数组");
print(IntArrayToString(rs));
/**将int[]转换成逗号分隔便于显示的辅助函数*/
private String IntArrayToString(int[] ints)
{
    StringBuilder str=new StringBuilder();
    str.append(‘[‘);
    for (int i:ints)
    {
        str.append(i);
        str.append(‘,‘);
    }
    str.deleteCharAt(str.length()-1);
    str.append(‘]‘);
    return str.toString();
}

运行结果:

例子2:使用JNI对例1返回的数组进行排序

//声明Java Native函数,参数为int[]
 public native static void sortIntArray(int []ints);
//实现C++函数
JNIEXPORT void JNICALL Java_com_example_wastrel_test_Test_sortIntArray
        (JNIEnv *env, jclass clazz, jintArray array){
        //获得传递过来的数组长度
        jsize size=env->GetArrayLength(array);
        //将数组转换成Java指针
        jint* jints=env->GetIntArrayElements(array,NULL);
        //简单的冒泡排序
        for (int i = 0; i <size-1 ; ++i) {
                for (int j = 0; j <size-1-i ; ++j) {
                        if(jints[j]<jints[j+1])
                        {
                                int t=jints[j];
                                jints[j]=jints[j+1];
                                jints[j+1]=t;
                        }
                }
        }
        //将排序结果更新到Java数组中,第三个参数等于0表明更新到原数组并释放所有元素
        env->ReleaseIntArrayElements(array,jints,0);
        return;
}
//在Java中调用
print("通过JNI对int数组排序:");
Test.sortIntArray(rs);
print(IntArrayToString(rs));

这里可以看到,我们并没有返回数组,而是通过ReleaseIntArrayElements函数将结果更新到Java数组中。随之我们在Java中的数组值已经变更。

运行结果:

其他基础类型数组的传递

其他基础类型中的数组传递与int[]几乎一致,函数名更换成各自的类型即可。这里不在过多叙述,我想有了上面的例子,很容易明白别的基础数据类型传递使用。

示例程序下载地址:http://download.csdn.net/detail/venusic/9604128

时间: 2024-08-04 00:06:10

Android Studio NDK 入门教程(2)--Java与C++之间的简单数据转换与传递的相关文章

Android Studio NDK 入门教程(5)--Java对象的传递与修改

概述 本文主要Java与C++之间的对象传递与取值.包括传递Java对象.返回Java对象.修改Java对象.以及性能对比. 通过JNIEnv完成数据转换 Java对象是存在于JVM虚拟机中的,而C++是脱离JVM而运行的,如果在C++中访问和使用Java中的对象,必然会使用JNIEnv这个桥梁.其实通过下面的代码很容易看出,这种访问方式和Java中的反射十分雷同. 这里定义一个简单Java对象用于下文测试: package com.example.wastrel.hellojni; /** *

Android Studio NDK 入门教程(6)--JNI签名验证防止恶意调用

概述 根据前面的文章来看,JNI其实只实现了关键代码加密,如果别人拿到了你的Java Native方法定义和对应的so,即可完成对你so里方法的调.因为native 方法和类都是不能混淆的,混淆了方法的函数名就变了,调用的时候就找不到方法了,因此如果反编译APK可以非常容易拿到相关文件和代码. 显然我们需要一些手段来在JNI的验证请求接口的是不是我们的程序. 签名验证的原理 可以用如下图来表明加了验证之后调用JNI的逻辑,用一个isValid 来表明请求的应用是不是我们自己的应用.isValid

Android Studio NDK 学习之接受Java传入的Int数组

本博客是基于Android Studio 1.3 preview版本,且默认你已经安装了Android SDK, Android NDK. 用Android Studio新建一个工程叫AndroidJNI_IntArray,其目录结构如下: ├── AndroidJNI_IntArray.iml ├── app │   ├── app.iml │   ├── build │   ├── build.gradle │   ├── libs │   ├── proguard-rules.pro │ 

Android Studio NDK 学习之接受Java传入的字符串

本博客是基于Android Studio 1.3 preview版本,且默认你已经安装了Android SDK, Android NDK. 用Android Studio新建一个工程叫Prompt,其目录结构如下: ├── Prompt.iml ├── app │   ├── app.iml │   ├── build │   ├── build.gradle │   ├── libs │   ├── proguard-rules.pro │   └── src ├── build │   └─

Android Studio NDK开发-JNI调用Java方法

相对于NDK来说SDK里面有更多API可以调用,有时候我们在做NDK开发的时候,需要在JNI直接Java中的方法和变量,比如callback,系统信息等.... 如何在JNI中调用Java方法呢?就需要先了解FindClass和GetMethodID了. FindClass和GetMethodID 在JNI中可以通过FindClass可以找到Java类,得到jclass,例如: jclass clz=(*env)->FindClass(env,"com/jjz/JniHandle"

eclipse再见,android studio 新手入门教程(二)项目的导入

上一篇博客介绍了AS的一些常用设置方法,当工具调教妥当后,自然就要开始项目的开发啦.从零开始新建一个项目,这个简单,不必多说,这篇博客会分享我从旧平台eclipse导入项目到AS的过程,以及遇到的一些问题并如何解决.开篇先粗略的提一些需要注意的地方. 结构目录 和eclipse不同,在android 视图下的项目目录分为java,res和manifests. manifests目录存放清单文件,不必多说. java目录会默认生成三个文件夹,其中test为在本机执行单元测试代码的目录, andro

android studio NDK 开发初探

android studio NDK 开发初探 环境配置 1)下载ndk 2)在android studio中配置ndk 路径 3)配置gradle 在gradle.properties中加入 android.useDeprecatedNdk=true配置 开启ndk 在开发过程中我们有时还会用到c++中到stl库这时我们就需要在build.gradle 中加入 defaultConfig { ndk { moduleName "jnitest" stl "stlport_s

Android Studio NDK开发

整理完Eclipse的NDK开发,再整理下Android Studio的.. 一个比较不错的百度网盘: http://pan.baidu.com/share/home?uk=2383159761  经常更新最新的Android方面的开发包,可以到里面下载到ndk的包 创建一个Android的工程,放一个TextView用于显示文字,功能很简单,从native层获取字符串并显示到TextView上 然后编写相关代码: public class MainActivity extends AppCom

[Learn Android Studio 汉化教程]第四章 : Refactoring Code

[Learn Android Studio 汉化教程]第四章 : Refactoring Code 第四章 Refactoring Code    重构代码 在Android Studio中开发,解决方案不会总是一蹴而成的.作为一个有效率的编程者,在你的开发,调试和测试中需要一些弹性以及代码重构.随着在这章中的行进,你将明白Android Studio如何产生代码:在这章里你将看到Android Studio如何重构你的代码.重构代码最大的风险是可能引入不期望的错误.通过分析某些风险重构操作的结