android-ndk 数据传递
2015-08-16 18:38
549 查看
首先定义DataProvider
定义好了Java类之后,接下来就要写本地代码。本地方法符号提供一个满足约定的头文件,使用Java工具Javah可以很容易地创建它而不用手动去创建。你对Java的class文件使用javah命令,就会为你生成一个对应的C/C++头文件。
1、在控制台下进入工作路径,本工程路径为:E:\work\java\workspace\testndkpassdata。
2、运行javah 命令:javah -classpath E:\work\java\workspace\JavaJni com.example.testndkpassdata ChangeMethodFromJni
本文生成的C/C++头文件名为: com_example_testndkpassdata_DataProvider.h
在C/C++中实现本地方法
生成C/C++头文件之后,你就需要写头文件对应的本地方法。注意:所有的本地方法的第一个参数都是指向JNIEnv结构的。这个结构是用来调用JNI函数的。第二个参数jclass的意义,要看方法是不是静态的(static)或者实例(Instance)的。前者,jclass代表一个类对象的引用,而后者是被调用的方法所属对象的引用。
返回值和参数类型根据等价约定映射到本地C/C++类型,如表JNI类型映射所示。有些类型,在本地代码中可直接使用,而其他类型只有通过JNI调用操作。
![](http://img.blog.csdn.net/20150830170925780?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
数组:
JNI通过JNIEnv提供的操作Java数组的功能。它提供了两个函数:一个是操作java的简单型数组的,另一个是操作对象类型数组的。
因为速度的原因,简单类型的数组作为指向本地类型的指针暴露给本地代码。因此,它们能作为常规的数组存取。这个指针是指向实际的Java数组或者Java数组的拷贝的指针。另外,数组的布置保证匹配本地类型。
为了存取Java简单类型的数组,你就要要使用GetXXXArrayElements函数(见表B),XXX代表了数组的类型。这个函数把Java数组看成参数,返回一个指向对应的本地类型的数组的指针。
![](http://img.blog.csdn.net/20150830171050604?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
当你对数组的存取完成后,要确保调用相应的ReleaseXXXArrayElements函数,参数是对应Java数组和GetXXXArrayElements返回的指针。如果必要的话,这个释放函数会复制你做的任何变化(这样它们就反射到java数组),然后释放所有相关的资源。
为了使用java对象的数组,你必须使用GetObjectArrayElement函数和SetObjectArrayElement函数,分别去get,set数组的元素。GetArrayLength函数会返回数组的长度。
使用对象
JNI提供的另外一个功能是在本地代码中使用Java对象。通过使用合适的JNI函数,你可以创建Java对象,get、set 静态(static)和实例(instance)的域,调用静态(static)和实例(instance)函数。JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数。
表C列出了用以得到静态(static)和实例(instance)的域与方法的JNI函数。每个函数接受(作为参数)域或方法的类,它们的名称,符号和它们对应返回的jfieldID或jmethodID。
如果你有了一个类的实例,它就可以通过方法GetObjectClass得到,或者如果你没有这个类的实例,可以通过FindClass得到。符号是从域的类型或者方法的参数,返回值得到字符串,如表D所示。
头文件
bean文件
实现文件
package com.example.testndkpassdata; public class DataProvider { /** * 把两个java中的int传递给c语言, c语言处理完毕后,把相加的结果返回给java * @param x * @param y * @return */ public native int add(int x ,int y); public static native int sub(int x ,int y); public native char add(char x, char y); //String /** * 把java中的string传递给c语言, c语言获取到java中的string之后 ,在string后面添加 一个hello 字符串 * @param s * @return */ public native String sayHelloInC(String s); /** * 把java中的一个int数组 传递给c语言,c语言处理完毕这个java数组 * 把int数组中的每一个元素+10 ; * 然后把结果返回给java * @param iNum * @return */ public native int[] intMethod(int[] iNum); public native byte[] byteMethod(byte[] iByte); public native DiskInfo getStruct(); }编译生成C/C++头文件
定义好了Java类之后,接下来就要写本地代码。本地方法符号提供一个满足约定的头文件,使用Java工具Javah可以很容易地创建它而不用手动去创建。你对Java的class文件使用javah命令,就会为你生成一个对应的C/C++头文件。
1、在控制台下进入工作路径,本工程路径为:E:\work\java\workspace\testndkpassdata。
2、运行javah 命令:javah -classpath E:\work\java\workspace\JavaJni com.example.testndkpassdata ChangeMethodFromJni
本文生成的C/C++头文件名为: com_example_testndkpassdata_DataProvider.h
在C/C++中实现本地方法
生成C/C++头文件之后,你就需要写头文件对应的本地方法。注意:所有的本地方法的第一个参数都是指向JNIEnv结构的。这个结构是用来调用JNI函数的。第二个参数jclass的意义,要看方法是不是静态的(static)或者实例(Instance)的。前者,jclass代表一个类对象的引用,而后者是被调用的方法所属对象的引用。
返回值和参数类型根据等价约定映射到本地C/C++类型,如表JNI类型映射所示。有些类型,在本地代码中可直接使用,而其他类型只有通过JNI调用操作。
数组:
JNI通过JNIEnv提供的操作Java数组的功能。它提供了两个函数:一个是操作java的简单型数组的,另一个是操作对象类型数组的。
因为速度的原因,简单类型的数组作为指向本地类型的指针暴露给本地代码。因此,它们能作为常规的数组存取。这个指针是指向实际的Java数组或者Java数组的拷贝的指针。另外,数组的布置保证匹配本地类型。
为了存取Java简单类型的数组,你就要要使用GetXXXArrayElements函数(见表B),XXX代表了数组的类型。这个函数把Java数组看成参数,返回一个指向对应的本地类型的数组的指针。
当你对数组的存取完成后,要确保调用相应的ReleaseXXXArrayElements函数,参数是对应Java数组和GetXXXArrayElements返回的指针。如果必要的话,这个释放函数会复制你做的任何变化(这样它们就反射到java数组),然后释放所有相关的资源。
为了使用java对象的数组,你必须使用GetObjectArrayElement函数和SetObjectArrayElement函数,分别去get,set数组的元素。GetArrayLength函数会返回数组的长度。
使用对象
JNI提供的另外一个功能是在本地代码中使用Java对象。通过使用合适的JNI函数,你可以创建Java对象,get、set 静态(static)和实例(instance)的域,调用静态(static)和实例(instance)函数。JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数。
表C列出了用以得到静态(static)和实例(instance)的域与方法的JNI函数。每个函数接受(作为参数)域或方法的类,它们的名称,符号和它们对应返回的jfieldID或jmethodID。
如果你有了一个类的实例,它就可以通过方法GetObjectClass得到,或者如果你没有这个类的实例,可以通过FindClass得到。符号是从域的类型或者方法的参数,返回值得到字符串,如表D所示。
头文件
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_testndkpassdata_DataProvider */ #ifndef _Included_com_example_testndkpassdata_DataProvider #define _Included_com_example_testndkpassdata_DataProvider #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_testndkpassdata_DataProvider * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_example_testndkpassdata_DataProvider_add__II (JNIEnv *, jobject, jint, jint); /* * Class: com_example_testndkpassdata_DataProvider * Method: sub * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_example_testndkpassdata_DataProvider_sub (JNIEnv *, jclass, jint, jint); /* * Class: com_example_testndkpassdata_DataProvider * Method: add * Signature: (CC)C */ JNIEXPORT jchar JNICALL Java_com_example_testndkpassdata_DataProvider_add__CC (JNIEnv *, jobject, jchar, jchar); /* * Class: com_example_testndkpassdata_DataProvider * Method: sayHelloInC * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_testndkpassdata_DataProvider_sayHelloInC (JNIEnv *, jobject, jstring); /* * Class: com_example_testndkpassdata_DataProvider * Method: intMethod * Signature: ([I)[I */ JNIEXPORT jintArray JNICALL Java_com_example_testndkpassdata_DataProvider_intMethod (JNIEnv *, jobject, jintArray); /* * Class: com_example_testndkpassdata_DataProvider * Method: byteMethod * Signature: ([B)[B */ JNIEXPORT jbyteArray JNICALL Java_com_example_testndkpassdata_DataProvider_byteMethod (JNIEnv *, jobject, jbyteArray); JNIEXPORT jobject JNICALL Java_com_example_testndkpassdata_DataProvider_getStruct (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
bean文件
package com.example.testndkpassdata; public class DiskInfo { public String name; public int serial; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSerial() { return serial; } public void setSerial(int serial) { this.serial = serial; } }
实现文件
#include<stdio.h> #include<jni.h> #include "com_example_testndkpassdata_DataProvider.h"; #include <android/log.h> #include<malloc.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_testndkpassdata_DataProvider_add__II( 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_testndkpassdata_DataProvider_sayHelloInC( JNIEnv * env, jobject obj, jstring jstr) { /*//在c语言中 是没有java的String char* cstr = Jstring2CStr(env, jstr); LOGD("cstr=%s",cstr); // c语言中的字符串 都是以'/0' 作为结尾 char arr[7]= {' ','h','e','l','l','o','\0'}; strcat(cstr,arr); LOGD("new cstr=%s",cstr); return (*env)->NewStringUTF(env,cstr);*/ const char* szStr = (*env)->GetStringUTFChars(env, jstr, 0); // c语言中的字符串 都是以'/0' 作为结尾 char arr[7] = { ' ', 'h', 'e', 'l', 'l', 'o', '\0' }; strcat(szStr, arr); (*env)->ReleaseStringUTFChars(env, jstr, szStr); return (*env)->NewStringUTF(env, szStr); } /**env java 虚拟机 结构体c实现的指针 包含的有很多jni方法 *jobject obj 代表的是调用这个c代码的java对象 代表的是DataProider的对象 */ JNIEXPORT jintArray JNICALL Java_com_example_testndkpassdata_DataProvider_intMethod( JNIEnv * env, jobject obj, jintArray arr) { //1.知道数组的长度 //2.操作这个数组里面的每一个元素 int len = (*env)->GetArrayLength(env, arr); LOGD("shuzu len =%d", len); // jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); jint* intarr = (*env)->GetIntArrayElements(env, arr, NULL); int i = 0; //c99 for (; i < len; i++) { //*(intarr+i) += 10; LOGD("intarr[%d]=%d", i, intarr[i]); intarr[i] += 10; } // void (*ReleaseIntArrayElements)(JNIEnv*, jintArray, // jint*, jint); // (*env)->ReleaseIntArrayElements(env, arr, intarr, NULL); // c语言释放掉 刚才申请的内存空间 return arr; } /** * 代表的是调用c代码 的class类 * jclass DataProvider 类 */ JNIEXPORT jint JNICALL Java_com_example_testndkpassdata_DataProvider_sub( JNIEnv * env, jclass clazz, jint x, jint y) { LOGD("x=%d", x); LOGD("y=%d", y); return x - y; } //返回一个结构,这里返回一个硬盘信息的简单结构类型 JNIEXPORT jobject JNICALL Java_com_example_testndkpassdata_DataProvider_getStruct( JNIEnv *env, jobject obj) { /* 下面为获取到Java中对应的实例类中的变量*/ //获取Java中的实例类 jclass objectClass = (*env)->FindClass(env, "com/example/testndkpassdata/DiskInfo"); jmethodID m = (*env)->GetMethodID(env,objectClass,"<init>","()V"); jobject Diskobj=(*env)->NewObject(env,objectClass,m); //获取类中每一个变量的定义 //名字 jfieldID str = (*env)->GetFieldID(env,objectClass, "name", "Ljava/lang/String;"); //序列号 jfieldID ival = (*env)->GetFieldID(env,objectClass, "serial", "I"); //给每一个实例的变量付值 (*env)->SetObjectField(env,Diskobj, str, (*env)->NewStringUTF(env,"my name is D:")); (*env)->SetIntField(env,Diskobj, ival, 10); // (*env)->DeleteLocalRef(env, m); // (*env)->DeleteLocalRef(env, str); // (*env)->DeleteLocalRef(env, ival); // (*env)->DeleteLocalRef(env, objectClass); return Diskobj; }测试文件
package com.example.testndkpassdata; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener{ static{ System.loadLibrary("Hello"); } private Button bt1,bt2,bt3,bt4,bt5; private DataProvider provider; private DiskInfo diskInfo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt1 = (Button) this.findViewById(R.id.bt1); bt2 = (Button) this.findViewById(R.id.bt2); bt3 = (Button) this.findViewById(R.id.bt3); bt4 = (Button) this.findViewById(R.id.bt4); bt5=(Button)this.findViewById(R.id.bt5); bt1.setOnClickListener(this); bt2.setOnClickListener(this); bt3.setOnClickListener(this); bt4.setOnClickListener(this); bt5.setOnClickListener(this); provider = new DataProvider(); diskInfo=new DiskInfo(); } public void onClick(View v) { switch (v.getId()) { case R.id.bt1: int result = provider.add(3, 5); Toast.makeText(this, "相加的结果"+result, 1).show(); break; case R.id.bt2: String str = provider.sayHelloInC("zhangsan "); Toast.makeText(this, str, 1).show(); break; case R.id.bt3: int[] arr = {1,2,3,4,5}; provider.intMethod(arr); for(int i=0;i<arr.length;i++){ Toast.makeText(this, "java "+ arr[i], 1).show(); System.out.println("java "+ arr[i]); } break; case R.id.bt4: int subresult = DataProvider.sub(5, 3); Toast.makeText(this, "相减的结果"+subresult, 1).show(); break; case R.id.bt5: DiskInfo diskInfo=provider.getStruct(); Toast.makeText(this, "结果name:"+diskInfo.getName()+"\nserial:"+diskInfo.getSerial(), 1).show(); break; } } }demo下载地址http://download.csdn.net/detail/maweisky531/9064183
相关文章推荐
- android paopao窗口+gridview 的实现底部菜单
- Android性能优化典范 集
- 【Android高级】Android系统以及Activity启动讲解
- Android系统设置大号字体后布局错乱的问题
- android开发之Menu的使用
- Android获得当前系统时间、星期几、周几
- Android中的测试
- Android中自定义Toast
- AndroidFrameWork
- 【Android高级】Dalvik虚拟机及其类加载器讲解
- android图片选择由于版本导致的oom解决办法
- android初学者的探索之路(Android音乐播放器二)
- Android ADB工具-进行文件操作(三)
- Android LayoutInflater原理分析,带你一步步深入了解View(一)
- Android实现带动画的下拉刷新RecyclerView
- android中一个应用程序启动另外一个应用程序,并传递数据。
- Android Studio 初次使用
- Android 控件沿贝塞尔曲线运动(上)
- Android知识点清单(不断更新)
- Android自定义照相机实现(拍照、保存到SD卡,利用Bundle在Acitivity交换数据)