Android NDK 开发(四)java传递数据到C【转】
转载请注明出处:http://blog.csdn.net/allen315410/article/details/41845701
前面几篇文章介绍了Android NDK开发的简单概念、常见错误及处理和从第一个Hello World开始实际做一个简单的JNI开发示例,相信看完之后,大家对NDK开发有了一个概念上的认识了,那么接下来我们需要再深入一下NDK的开发,我们知道NDK开发就是使用JNI这层“协议”在Java和C之间起个“桥梁”的作用,将Java和Native C之间联立起来,让Java和C直接的数据进行互调。谈到Java和C之间的数据调用,那么Java是怎样传递数据到C中的呢,C拿到数据处理完后又怎样将处理后的数据回传给Java的呢?先别急,接下来我们就看看Java怎么传递数据给C的。
1,建立一个Android工程,在工程下建立一个DataProvider类,在这个类里定义3个native方法,如下:
- package com.example.ndktransferdata;
- public class DataProvider {
- /**
- * 把两个java中的int传递给C语言,c语言处理完毕后,把相加的结果返回给java
- *
- * @param x
- * @param y
- * @return
- */
- public native int add(int x, int y);
- /**
- * 把java中的String传递给c语言,c语言获取后,在string后面添加一个hello字符串,返回给java
- *
- * @param s
- * @return
- */
- public native String sayHelloInC(String s);
- /**
- * 把java中的一个int数组传递给C语言,C语言接收这个数组,把int数组中的每一个元素+10,然后返回给Java
- *
- * @param iNum
- * @return
- */
- public native int[] intMethod(int[] iNum);
- }
2,用Javah编译头文件
做到这一步发现了一个问题,从描述上看应该是编码错误,这里错误的使用了GBK来编译java文件了,改成UTF-8就没问题,只要在javah命令后面添加 -encoding utf-8,为编译器提供编码环境就行,下面是改正后的结果:
说明native代码的函数签名已经生成了,我们将这个生成的函数签名头文件剪切到jni目录下,在c代码中引用这个头文件就好了。
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_example_ndktransferdata_DataProvider */
- #ifndef _Included_com_example_ndktransferdata_DataProvider
- #define _Included_com_example_ndktransferdata_DataProvider
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_example_ndktransferdata_DataProvider
- * Method: add
- * Signature: (II)I
- */
- JNIEXPORT jint JNICALL Java_com_example_ndktransferdata_DataProvider_add
- (JNIEnv *, jobject, jint, jint);
- /*
- * Class: com_example_ndktransferdata_DataProvider
- * Method: sayHelloInC
- * Signature: (Ljava/lang/String;)Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_example_ndktransferdata_DataProvider_sayHelloInC
- (JNIEnv *, jobject, jstring);
- /*
- * Class: com_example_ndktransferdata_DataProvider
- * Method: intMethod
- * Signature: ([I)[I
- */
- JNIEXPORT jintArray JNICALL Java_com_example_ndktransferdata_DataProvider_intMethod
- (JNIEnv *, jobject, jintArray);
- #ifdef __cplusplus
- }
- #endif
- #endif
3,编写C语言代码
之前在第二步的时候我们编译好了函数签名的头文件,所以这里我们就需要用过头文件中的方法签名了,一共包含3个这样的native函数,函数里实现是这样的:
- #include<stdio.h>
- #include<jni.h>
- #include<malloc.h>
- #include<string.h>
- #include"com_example_ndktransferdata_DataProvider.h"
- #include<android/log.h>
- #define LOG_TAG "System.out.c"
- #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- /**
- * 返回值 char* 这个代表char数组的首地址
- * Jstring2CStr 把java中的jstring的类型转化成一个c语言中的char 字符串
- */
- char* Jstring2CStr(JNIEnv* env, jstring jstr) {
- char* rtn = NULL;
- jclass clsstring = (*env)->FindClass(env, "java/lang/String"); //String
- jstring strencode = (*env)->NewStringUTF(env, "GB2312"); // 得到一个java字符串 "GB2312"
- jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
- "(Ljava/lang/String;)[B"); //[ String.getBytes("gb2312");
- jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
- strencode); // String .getByte("GB2312");
- jsize alen = (*env)->GetArrayLength(env, barr); // byte数组的长度
- jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
- if (alen > 0) {
- rtn = (char*) malloc(alen + 1); //"\0"
- memcpy(rtn, ba, alen);
- rtn[alen] = 0;
- }
- (*env)->ReleaseByteArrayElements(env, barr, ba, 0); //
- return rtn;
- }
- JNIEXPORT jint JNICALL Java_com_example_ndktransferdata_DataProvider_add(
- JNIEnv * env, jobject obj, jint x, jint y) {
- LOGD("x = %d", x);
- LOGD("y = %d", y);
- return x + y;
- }
- JNIEXPORT jstring JNICALL Java_com_example_ndktransferdata_DataProvider_sayHelloInC(
- JNIEnv * env, jobject obj, jstring jstr) {
- char* cstr = Jstring2CStr(env, jstr);
- LOGD("cstr = %s", cstr);
- char arr[7] = { ' ', 'h', 'e', 'l', 'l', 'o', '\0' };
- strcat(cstr, arr);
- LOGD("new cstr = %s", cstr);
- return (*env)->NewStringUTF(env, cstr);
- }
- JNIEXPORT jintArray JNICALL Java_com_example_ndktransferdata_DataProvider_intMethod(
- JNIEnv * env, jobject obj, jintArray jarr) {
- //获取传递进来数组的长度
- int len = (*env)->GetArrayLength(env, jarr);
- //获取传递进来数组的元素,即数组首地址
- jint* intArr = (*env)->GetIntArrayElements(env, jarr, 0);
- int i = 0;
- for (; i < len; i++) {
- //打印处理前的数组元素
- LOGD("intArr[%d] = %d", i, intArr[i]);
- //遍历数组元素+10
- *(intArr + i) += 10;
- }
- return jarr;
- }
4,配置Android.mk文件
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := Hello
- LOCAL_SRC_FILES := Hello.c
- LOCAL_LDLIBS += -llog
- include $(BUILD_SHARED_LIBRARY)
光配置Android.mk文件大致就可以了,但是还需要解决一个版本兼容问题,做法是在jni目录下新建Application.mk文件,加上
- APP_PLATFORM := android-8
5,编译C语言代码
6,Java代码中处理返回的数据
Java中传递数据到C代码中,C代码处理完后返回给Java,这时候Java拿到数据后就可以做自己的一些业务操作了,首先我们编译完Native代码后,先Refresh一下工程,然后clean一下工程,编写如下的测试案例:
- public class MainActivity extends Activity implements OnClickListener {
- // 加载本地库文件
- static {
- System.loadLibrary("Hello");
- }
- private Button btn1, btn2, btn3;
- private DataProvider provider;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- btn1 = (Button) findViewById(R.id.btn1);
- btn2 = (Button) findViewById(R.id.btn2);
- btn3 = (Button) findViewById(R.id.btn3);
- btn1.setOnClickListener(this);
- btn2.setOnClickListener(this);
- btn3.setOnClickListener(this);
- provider = new DataProvider();
- }
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.btn1 : // 传递2个int给C代码
- int result = provider.add(3, 5);
- Toast.makeText(this, "相加的结果:" + result, 0).show();
- break;
- case R.id.btn2 : // 传递string给C代码
- String str = provider.sayHelloInC("zhang san");
- Toast.makeText(this, str, 0).show();
- break;
- case R.id.btn3 : // 传递int数组给C代码
- int[] arr = {1, 2, 3, 4, 5};
- provider.intMethod(arr);
- for (int i = 0; i < arr.length; i++) {
- System.out.println("arr[" + i + "] = " + arr[i]);
- }
- break;
- default :
- break;
- }
- }
- }
运行一下工程,注意:这里只能开启arm模拟器,如果是x86模拟器会安装apk时候报错,因为这段native代码只编写了arm支持的版本,没有支持x86。如果在运行测试的时候出现一些错误的话,请参考上篇文章中提示慢慢解决。Android NDK开发——常见错误集锦以及LOG使用
测试1:Java传递2个int给C
Logcat输出:
测试2:Java传递string给C
Logcat输出:
测试3:Java传递int数组给C
Logcat输出:
总结:
上述这个简单的示例可以说明ndk开发中,Java是怎样将数据传递给C代码的了,程序中只是简单的介绍了3种数据类型int,string和int[],这是远远不够的,因为Java支持的数据类型比较多,这时候怎么办?好,有了上面的例子,我们可以举一反三了,在之前的博客中我也强调过ndk解压包下的jni.h这个文件的重要性,这个文件不仅仅定义了Java数据类型在C语言中的表示,看一下源码,就发现一种一一映射的关系:
- ......
- typedef uint8_t jboolean; /* unsigned 8 bits */
- typedef int8_t jbyte; /* signed 8 bits */
- typedef uint16_t jchar; /* unsigned 16 bits */
- typedef int16_t jshort; /* signed 16 bits */
- typedef int32_t jint; /* signed 32 bits */
- typedef int64_t jlong; /* signed 64 bits */
- typedef float jfloat; /* 32-bit IEEE 754 */
- typedef double jdouble; /* 64-bit IEEE 754 */
- #else
- 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 */
- ......
另外还有一个非常重要的结构体JNINativeInterface,这里面定义了很多C函数,只要看懂大致意思就可以试着去调用这些函数,这些函数在native开发中显得特别重要:
- ......
- jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
- jbyteArray (*NewByteArray)(JNIEnv*, jsize);
- jcharArray (*NewCharArray)(JNIEnv*, jsize);
- jshortArray (*NewShortArray)(JNIEnv*, jsize);
- jintArray (*NewIntArray)(JNIEnv*, jsize);
- jlongArray (*NewLongArray)(JNIEnv*, jsize);
- jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
- jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
- jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
- jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
- jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
- jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
- jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
- jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
- jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
- jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
- ......
源码比较长,有兴趣的朋友自己翻看一下,这里只贴部分。
Android NDK 开发(四)java传递数据到C【转】的更多相关文章
- Android NDK开发 JNI操作java构造方法,普通方法,静态方法(七)
Android NDK开发 JNI操作java普通.静态.构造方法 1.Jni实例化一个Java类的实例jobject 1.通过FindClas( ),获取Java类的的jclass 2.通过GetM ...
- !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
http://hujiaweibujidao.github.io/blog/2013/11/18/android-ndk-and-opencv-development-3/ Android Ndk a ...
- Android NDK开发初识
神秘的Android NDK开发往往众多程序员感到兴奋,但又不知它为何物,由于近期开发应用时,为了是开发的.apk文件不被他人解读(反编译),查阅了很多资料,其中有提到使用NDK开发,怀着好奇的心理, ...
- Android NDK开发
Android NDK 开发教程(极客学院) 一.Android NDK环境搭建 使用最新ndk,直接抛弃cygwin,以前做Android的项目要用到NDK就必须要下载NDK,下载安装Cygwin( ...
- Android NDK 开发(三)--常见错误锦集合Log的使用【转】
转载请注明出处:http://blog.csdn.net/allen315410/article/details/41826511 Android NDK开发经常因某些因素会出现一些意想不到的错误, ...
- android NDK开发环境搭建
android NDK开发环境搭建 2012-05-14 00:13:58 分类: 嵌入式 基于 Android NDK 的学习之旅-----环境搭建 工欲善其事必先利其器 , 下面介绍下 Eclip ...
- 跟我学Android NDK开发(一)
Android NDK 开发跟其它开发一样,首先需要配置好开发环境,本文以 Ubuntu系统为例介绍如何进行 Android NDK 开发环境的配置. 1. 简介 什么是 Android NDK 呢? ...
- android NDK开发在本地C/C++源码中设置断点单步调试具体教程
近期在学android NDK开发,折腾了一天,最终可以成功在ADT中设置断点单步调试本地C/C++源码了.网上关于这方面的资料太少了,并且大都不全,并且调试过程中会出现各种各样的问题,真是非常磨人. ...
- windows下用ADT进行android NDK开发的具体教程(从环境搭建、配置到编译全过程)
郑重申明:如需转载本博客,请注明出处,谢谢! 这几天在学习android NDK的开发.那么首先让我们来看看android NDK开发的本质是什么. NDK(Native Development Ki ...
随机推荐
- search搜索功能
1.html <div class="search"> <form name="formsearch" action=" ...
- 05-雷海林-mysql备份原理与在TDSQL中的实践
05-雷海林-mysql备份原理与在TDSQL中的实践 下载地址: http://files.cnblogs.com/files/MYSQLZOUQI/05-%E9%9B%B7%E6%B5%B7%E6 ...
- Android EditText 改变边框颜色
第一步:为了更好的比较,准备两个一模一样的EditText(当Activity启动时,焦点会在第一个EditText上,如果你不希望这样只需要写一个高度和宽带为0的EditText即可避免,这里就不这 ...
- SQL截取字符串函数
A.截取从字符串左边开始N个字符 以下是代码片段: Declare @S1 varchar(100) Select @S1='http://www.xrss.cn' Select Left( ...
- Android --Activity与Fragment通讯
参考博客:详解Fragment跟Activity之间的通信 Activity中方法 private OnSearchListener mSearchListener; /** *定义一个借口 **/ ...
- linux type命令用法_转
转自:http://codingstandards.iteye.com/blog/831504 在脚本中type可用于检查命令或函数是否存在,存在返回0,表示成功:不存在返回正值,表示不成功. $ t ...
- iOS UICollectionView之三(基本用法)
#import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate> @pr ...
- iOS -Swift 3.0 -UIButton属性大全
// // ViewController.swift // Swift-UIButton // // Created by luorende on 16/9/9. // Copyright © ...
- qunit.js初试
看了下mbraak-simple-data-grid写的单元测试,感觉还是很好入手的 用module函数定义模块 用test函数定义测试方法 用equal.ok(判断是否为真)等方法做断言判断 用se ...
- c#操作Excel时,抛出异常:“未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序”
我们开发环境下,使用excel导入数据到数据库中,编译的软件起初是x86 方式,起初并未发现什么问题,一切很正常: 程序该进的过程: 后来导入文件一次就要读取几百G的数据导入数据库中,使用编译的X86 ...