您的位置:首页 > 移动开发 > Android开发

android-ndk 数据传递

2015-08-16 18:38 549 查看
首先定义DataProvider

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: