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方法,如下:

[java] view plain copy

print?

  1. package com.example.ndktransferdata;
  2. public class DataProvider {
  3. /**
  4. * 把两个java中的int传递给C语言,c语言处理完毕后,把相加的结果返回给java
  5. *
  6. * @param x
  7. * @param y
  8. * @return
  9. */
  10. public native int add(int x, int y);
  11. /**
  12. * 把java中的String传递给c语言,c语言获取后,在string后面添加一个hello字符串,返回给java
  13. *
  14. * @param s
  15. * @return
  16. */
  17. public native String sayHelloInC(String s);
  18. /**
  19. * 把java中的一个int数组传递给C语言,C语言接收这个数组,把int数组中的每一个元素+10,然后返回给Java
  20. *
  21. * @param iNum
  22. * @return
  23. */
  24. public native int[] intMethod(int[] iNum);
  25. }

2,用Javah编译头文件

做到这一步发现了一个问题,从描述上看应该是编码错误,这里错误的使用了GBK来编译java文件了,改成UTF-8就没问题,只要在javah命令后面添加 -encoding utf-8,为编译器提供编码环境就行,下面是改正后的结果:

说明native代码的函数签名已经生成了,我们将这个生成的函数签名头文件剪切到jni目录下,在c代码中引用这个头文件就好了。

[cpp] view plain copy

print?

  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class com_example_ndktransferdata_DataProvider */
  4. #ifndef _Included_com_example_ndktransferdata_DataProvider
  5. #define _Included_com_example_ndktransferdata_DataProvider
  6. #ifdef __cplusplus
  7. extern "C" {
  8. #endif
  9. /*
  10. * Class:     com_example_ndktransferdata_DataProvider
  11. * Method:    add
  12. * Signature: (II)I
  13. */
  14. JNIEXPORT jint JNICALL Java_com_example_ndktransferdata_DataProvider_add
  15. (JNIEnv *, jobject, jint, jint);
  16. /*
  17. * Class:     com_example_ndktransferdata_DataProvider
  18. * Method:    sayHelloInC
  19. * Signature: (Ljava/lang/String;)Ljava/lang/String;
  20. */
  21. JNIEXPORT jstring JNICALL Java_com_example_ndktransferdata_DataProvider_sayHelloInC
  22. (JNIEnv *, jobject, jstring);
  23. /*
  24. * Class:     com_example_ndktransferdata_DataProvider
  25. * Method:    intMethod
  26. * Signature: ([I)[I
  27. */
  28. JNIEXPORT jintArray JNICALL Java_com_example_ndktransferdata_DataProvider_intMethod
  29. (JNIEnv *, jobject, jintArray);
  30. #ifdef __cplusplus
  31. }
  32. #endif
  33. #endif

3,编写C语言代码

之前在第二步的时候我们编译好了函数签名的头文件,所以这里我们就需要用过头文件中的方法签名了,一共包含3个这样的native函数,函数里实现是这样的:

[cpp] view plain copy

print?

  1. #include<stdio.h>
  2. #include<jni.h>
  3. #include<malloc.h>
  4. #include<string.h>
  5. #include"com_example_ndktransferdata_DataProvider.h"
  6. #include<android/log.h>
  7. #define LOG_TAG "System.out.c"
  8. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
  9. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  10. /**
  11. * 返回值 char* 这个代表char数组的首地址
  12. *  Jstring2CStr 把java中的jstring的类型转化成一个c语言中的char 字符串
  13. */
  14. char* Jstring2CStr(JNIEnv* env, jstring jstr) {
  15. char* rtn = NULL;
  16. jclass clsstring = (*env)->FindClass(env, "java/lang/String"); //String
  17. jstring strencode = (*env)->NewStringUTF(env, "GB2312"); // 得到一个java字符串 "GB2312"
  18. jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
  19. "(Ljava/lang/String;)[B"); //[ String.getBytes("gb2312");
  20. jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
  21. strencode); // String .getByte("GB2312");
  22. jsize alen = (*env)->GetArrayLength(env, barr); // byte数组的长度
  23. jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
  24. if (alen > 0) {
  25. rtn = (char*) malloc(alen + 1); //"\0"
  26. memcpy(rtn, ba, alen);
  27. rtn[alen] = 0;
  28. }
  29. (*env)->ReleaseByteArrayElements(env, barr, ba, 0); //
  30. return rtn;
  31. }
  32. JNIEXPORT jint JNICALL Java_com_example_ndktransferdata_DataProvider_add(
  33. JNIEnv * env, jobject obj, jint x, jint y) {
  34. LOGD("x = %d", x);
  35. LOGD("y = %d", y);
  36. return x + y;
  37. }
  38. JNIEXPORT jstring JNICALL Java_com_example_ndktransferdata_DataProvider_sayHelloInC(
  39. JNIEnv * env, jobject obj, jstring jstr) {
  40. char* cstr = Jstring2CStr(env, jstr);
  41. LOGD("cstr = %s", cstr);
  42. char arr[7] = { ‘ ‘, ‘h‘, ‘e‘, ‘l‘, ‘l‘, ‘o‘, ‘\0‘ };
  43. strcat(cstr, arr);
  44. LOGD("new cstr = %s", cstr);
  45. return (*env)->NewStringUTF(env, cstr);
  46. }
  47. JNIEXPORT jintArray JNICALL Java_com_example_ndktransferdata_DataProvider_intMethod(
  48. JNIEnv * env, jobject obj, jintArray jarr) {
  49. //获取传递进来数组的长度
  50. int len = (*env)->GetArrayLength(env, jarr);
  51. //获取传递进来数组的元素,即数组首地址
  52. jint* intArr = (*env)->GetIntArrayElements(env, jarr, 0);
  53. int i = 0;
  54. for (; i < len; i++) {
  55. //打印处理前的数组元素
  56. LOGD("intArr[%d] = %d", i, intArr[i]);
  57. //遍历数组元素+10
  58. *(intArr + i) += 10;
  59. }
  60. return jarr;
  61. }

4,配置Android.mk文件

[javascript] view plain copy

print?

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE    := Hello
  4. LOCAL_SRC_FILES := Hello.c
  5. LOCAL_LDLIBS += -llog
  6. include $(BUILD_SHARED_LIBRARY)

光配置Android.mk文件大致就可以了,但是还需要解决一个版本兼容问题,做法是在jni目录下新建Application.mk文件,加上

[javascript] view plain copy

print?

  1. APP_PLATFORM := android-8

5,编译C语言代码

6,Java代码中处理返回的数据

Java中传递数据到C代码中,C代码处理完后返回给Java,这时候Java拿到数据后就可以做自己的一些业务操作了,首先我们编译完Native代码后,先Refresh一下工程,然后clean一下工程,编写如下的测试案例:

[java] view plain copy

print?

  1. public class MainActivity extends Activity implements OnClickListener {
  2. // 加载本地库文件
  3. static {
  4. System.loadLibrary("Hello");
  5. }
  6. private Button btn1, btn2, btn3;
  7. private DataProvider provider;
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. setContentView(R.layout.activity_main);
  12. btn1 = (Button) findViewById(R.id.btn1);
  13. btn2 = (Button) findViewById(R.id.btn2);
  14. btn3 = (Button) findViewById(R.id.btn3);
  15. btn1.setOnClickListener(this);
  16. btn2.setOnClickListener(this);
  17. btn3.setOnClickListener(this);
  18. provider = new DataProvider();
  19. }
  20. @Override
  21. public void onClick(View v) {
  22. switch (v.getId()) {
  23. case R.id.btn1 : // 传递2个int给C代码
  24. int result = provider.add(3, 5);
  25. Toast.makeText(this, "相加的结果:" + result, 0).show();
  26. break;
  27. case R.id.btn2 : // 传递string给C代码
  28. String str = provider.sayHelloInC("zhang san");
  29. Toast.makeText(this, str, 0).show();
  30. break;
  31. case R.id.btn3 : // 传递int数组给C代码
  32. int[] arr = {1, 2, 3, 4, 5};
  33. provider.intMethod(arr);
  34. for (int i = 0; i < arr.length; i++) {
  35. System.out.println("arr[" + i + "] = " + arr[i]);
  36. }
  37. break;
  38. default :
  39. break;
  40. }
  41. }
  42. }

运行一下工程,注意:这里只能开启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语言中的表示,看一下源码,就发现一种一一映射的关系:

[cpp] view plain copy

print?

  1. ......
  2. typedef uint8_t         jboolean;       /* unsigned 8 bits */
  3. typedef int8_t          jbyte;          /* signed 8 bits */
  4. typedef uint16_t        jchar;          /* unsigned 16 bits */
  5. typedef int16_t         jshort;         /* signed 16 bits */
  6. typedef int32_t         jint;           /* signed 32 bits */
  7. typedef int64_t         jlong;          /* signed 64 bits */
  8. typedef float           jfloat;         /* 32-bit IEEE 754 */
  9. typedef double          jdouble;        /* 64-bit IEEE 754 */
  10. #else
  11. typedef unsigned char   jboolean;       /* unsigned 8 bits */
  12. typedef signed char     jbyte;          /* signed 8 bits */
  13. typedef unsigned short  jchar;          /* unsigned 16 bits */
  14. typedef short           jshort;         /* signed 16 bits */
  15. typedef int             jint;           /* signed 32 bits */
  16. typedef long long       jlong;          /* signed 64 bits */
  17. typedef float           jfloat;         /* 32-bit IEEE 754 */
  18. typedef double          jdouble;        /* 64-bit IEEE 754 */
  19. ......

另外还有一个非常重要的结构体JNINativeInterface,这里面定义了很多C函数,只要看懂大致意思就可以试着去调用这些函数,这些函数在native开发中显得特别重要:

[cpp] view plain copy

print?

  1. ......
  2. jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
  3. jbyteArray    (*NewByteArray)(JNIEnv*, jsize);
  4. jcharArray    (*NewCharArray)(JNIEnv*, jsize);
  5. jshortArray   (*NewShortArray)(JNIEnv*, jsize);
  6. jintArray     (*NewIntArray)(JNIEnv*, jsize);
  7. jlongArray    (*NewLongArray)(JNIEnv*, jsize);
  8. jfloatArray   (*NewFloatArray)(JNIEnv*, jsize);
  9. jdoubleArray  (*NewDoubleArray)(JNIEnv*, jsize);
  10. jboolean*   (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
  11. jbyte*      (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
  12. jchar*      (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
  13. jshort*     (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
  14. jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
  15. jlong*      (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
  16. jfloat*     (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
  17. jdouble*    (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
  18. ......

源码比较长,有兴趣的朋友自己翻看一下,这里只贴部分。

源码请在这里下载

时间: 2024-10-22 22:27:39

Android NDK 开发(四)java传递数据到C【转】的相关文章

Android NDK开发(四)——Java传递数据到C

转载请注明出处:http://blog.csdn.net/allen315410/article/details/41845701 前面几篇文章介绍了Android NDK开发的简单概念.常见错误及处理和从第一个Hello World开始实际做一个简单的JNI开发示例,相信看完之后,大家对NDK开发有了一个概念上的认识了,那么接下来我们需要再深入一下NDK的开发,我们知道NDK开发就是使用JNI这层"协议"在Java和C之间起个"桥梁"的作用,将Java和Nativ

Android NDK开发篇(五):Java与原生代码通信(数据操作)

尽管说使用NDK能够提高Android程序的运行效率,可是调用起来还是略微有点麻烦.NDK能够直接使用Java的原生数据类型,而引用类型,由于Java的引用类型的实如今NDK被屏蔽了,所以在NDK使用Java的引用类型则要做对应的处理. 一.对引用数据类型的操作 尽管Java的引用类型的实如今NDK被屏蔽了,JNI还是提供了一组API,通过JNIEnv接口指针提供原生方法改动和使用Java的引用类型. 1.字符串操作 JNI把Java的字符串当作引用来处理,在NDK中使用Java的字符串,须要相

Android NDK开发(八)——应用监听自身卸载,弹出用户反馈调查

转载请注明出处:http://blog.csdn.net/allen315410/article/details/42521251 监听卸载情景和原理分析 1,情景分析 在上上篇博客中我写了一下NDK开发实践项目,使用开源的LAME库转码MP3,作为前面几篇基础博客的加深理解使用的,但是这样的项目用处不大,除了练练NDK功底.这篇博客,我将讲述一下一个各大应用中很常见的一个功能,同样也是基于JNI开发的Android应用小Demo,看完这个之后,不仅可以加深对NDK开发的理解,而且该Demo也可

Android NDK开发(六)——使用开源LAME转码mp3

转载请注明出处:http://blog.csdn.net/allen315410/article/details/42456661 在本专栏的前面几篇博客中讲述了一些Android NDK开发的基础,从环境搭建一直到利用JNI进行Java端和C端代码的互相调用,并且的讲解的Demo也是很简单易懂的,相信掌握前面博客的大部分内容,就可以着手在实际项目中利用JNI进行NDK开发了,那么既然基础过了,接下来我在这里尝试去使用真实项目中去.我们知道,C语言因为高效,而且又是最早期的高级编程之一,一直存活

Android NDK 开发(三)--常见错误锦集合Log的使用【转】

转载请注明出处:http://blog.csdn.net/allen315410/article/details/41826511  Android NDK开发经常因某些因素会出现一些意想不到的错误,很多时候调试这些错误的时候,显得比调试Java代码要复杂,一方面是导致错误的原因很多很杂,另一方面NDK开发涉及到C/C++代码的编写,很多程序员对此不熟悉.那么这篇博客就总结一下,在NDK开发中经常出现的一些问题,并且尝试提供一些正确的解决方案,方便在开发时能够快速定位到错误,更改错误,当然了,错

Android NDK 开发

NDK全称:Native Development Kit. 1.NDK是一系列工具的集合. * NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk.这些工具对开发者的帮助是巨大的. * NDK集成了交叉编译器,并提供了相应的mk文件隔离平台.CPU.API等差异,开发人员只需要简单修改mk文件(指出"哪些文件需要编译"."编译特性要求"等),就可以创建出so. * NDK可以自动地将so和Java应用一起

跟我学Android NDK开发(一)

Android NDK 开发跟其它开发一样,首先需要配置好开发环境,本文以 Ubuntu系统为例介绍如何进行 Android NDK 开发环境的配置. 1. 简介 什么是 Android NDK 呢? NDK(Native Development Kit) 是一个允许开发者用一些本地语言(C/C++)编写 Android App 的部分功能的工具集.对于一些特定的 App,NDK 非常有利于我们直接使用现成的用 C/C++ 编写的代码库(但对于大多数 App 来说,NDK 是没有必要的).使用

Android ndk开发swig编译jni接口配置文件(二)

之前写过一篇Android ndk开发swig编译jni接口.看这篇看不懂,看以去看看.c++与Java有些语言结构还是有一定区别,比如c++结构体,一些函数的返回值等都是有所不同,进行swig编译要进行一些预处理,也就是配置一下就行.下面说说几种情况. 一.一般情况下string,数组,枚举类型等配置Unix.i %module Survey %include "std_string.i" %include "arrays_java.i" %include &qu

android NDK开发在本地C/C++源码中设置断点单步调试具体教程

近期在学android NDK开发,折腾了一天,最终可以成功在ADT中设置断点单步调试本地C/C++源码了.网上关于这方面的资料太少了,并且大都不全,并且调试过程中会出现各种各样的问题,真是非常磨人.程序员就得有耐心. 把自己的调试过程记录下来.希望对须要的朋友有帮助. 在看本文之前,请先确保你已经成功编译了一个android NDKproject,而且可以在模拟器或者真机上执行.至于怎么编译NDKproject,包含配置.生成.so文件等等.可以參考我的前一篇博客:http://blog.cs