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

Android使用JNI实现Java与C之间传递数据

2011-11-25 15:20 896 查看
介绍Java如何将数据传递给C和C回调Java的方法。 java传递数据给C,在C代码中进行处理数据,处理完数据后返回给java。C的回调是Java传递数据给C,C需要用到Java中的某个方法,就需要调用java的方法。

Android中使用JNI七个步骤:

1.创建一个android工程

2.JAVA代码中写声明native 方法 public native String helloFromJNI();

3.用javah工具生成头文件

4. 创建jni目录,引入头文件,根据头文件实现c代码

5.编写Android.mk文件

6.Ndk编译生成动态库

7.Java代码load 动态库.调用native代码

Java调用C进行数据传递

这里分别传递整形、字符串、数组在C中进行处理。

声明native 方法:

view plainprint?

public class DataProvider {

// 两个java中的int 传递c 语言 , c语言处理这个相加的逻辑,把相加的结果返回给java

public native int add(int x ,int y);

//把一个java中的字符串传递给c语言, c 语言处理下字符串, 处理完毕返回给java

public native String sayHelloInC(String s);

//把一个java中int类型的数组传递给c语言, c语言里面把数组的每一个元素的值 都增加5,

//然后在把处理完毕的数组,返回给java

public native int[] intMethod(int[] iNum);

}
public class DataProvider {

// 两个java中的int 传递c 语言 , c语言处理这个相加的逻辑,把相加的结果返回给java

public native int add(int x ,int y); //把一个java中的字符串传递给c语言, c 语言处理下字符串, 处理完毕返回给java

public native String sayHelloInC(String s);

//把一个java中int类型的数组传递给c语言, c语言里面把数组的每一个元素的值 都增加5, //然后在把处理完毕的数组,返回给java

public native int[] intMethod(int[] iNum);

}

以上方法要在C中实现的头文件,头文件可以理解为要在C中实现的方法

其中 JENEnv* 代表的是java环境 , 通过这个环境可以调用java的方法,jobject 表示哪个对象调用了 这个c语言的方法, thiz就表示的是当前的对象

view plainprint?

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class cn_itcast_ndk3_DataProvider */

#ifndef _Included_cn_itcast_ndk3_DataProvider

#define _Included_cn_itcast_ndk3_DataProvider

#ifdef __cplusplus extern "C" {

#endif

/* * Class: cn_itcast_ndk3_DataProvider * Method: add * Signature: (II)I */

JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add (JNIEnv *, jobject, jint, jint);

/* * Class: cn_itcast_ndk3_DataProvider * Method: sayHelloInC * Signature: (Ljava/lang/String;)Ljava/lang/String; */

JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC (JNIEnv *, jobject, jstring);

/* * Class: cn_itcast_ndk3_DataProvider * Method: intMethod * Signature: ([I)[I */

JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod (JNIEnv *, jobject, jintArray);

#ifdef __cplusplus

}

#endif

#endif
/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class cn_itcast_ndk3_DataProvider */

#ifndef _Included_cn_itcast_ndk3_DataProvider

#define _Included_cn_itcast_ndk3_DataProvider

#ifdef __cplusplus extern "C" {

#endif

/* * Class: cn_itcast_ndk3_DataProvider * Method: add * Signature: (II)I */

JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add (JNIEnv *, jobject, jint, jint);

/* * Class: cn_itcast_ndk3_DataProvider * Method: sayHelloInC * Signature: (Ljava/lang/String;)Ljava/lang/String; */

JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC (JNIEnv *, jobject, jstring);

/* * Class: cn_itcast_ndk3_DataProvider * Method: intMethod * Signature: ([I)[I */

JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod (JNIEnv *, jobject, jintArray);

#ifdef __cplusplus

}

#endif

#endif

C代码出了要引用头文件外,还要引入日志信息,以方便在C 中进行调试

view plainprint?

//引入头文件

#include "cn_itcast_ndk3_DataProvider.h"

#include <string.h> //导入日志头文件

#include <android/log.h> //修改日志tag中的值

#define LOG_TAG "logfromc" //日志显示的等级

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) // java中的jstring, 转化为c的一个字符数组

char* Jstring2CStr(JNIEnv* env, jstring jstr) {

char* rtn = NULL;

jclass clsstring = (*env)->FindClass(env,"java/lang/String");

jstring strencode = (*env)->NewStringUTF(env,"GB2312");

jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");

jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");

jsize alen = (*env)->GetArrayLength(env,barr);

jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);

if(alen > 0) {

rtn = (char*)malloc(alen+1); //new char[alen+1]; "\0"

memcpy(rtn,ba,alen);

rtn[alen]=0;

}

(*env)->ReleaseByteArrayElements(env,barr,ba,0); //释放内存

return rtn;

}

//处理整形相加

JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add (JNIEnv * env, jobject obj, jint x, jint y){

//打印 java 传递过来的 jstring ;

LOGI("log from c code ");

LOGI("x= %ld",x);

LOGD("y= %ld",y);

return x+y; }

//处理字符串追加

JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC (JNIEnv * env, jobject obj, jstring str){

char* p = Jstring2CStr(env,str);

LOGI("%s",p);

char* newstr = "append string"; //strcat(dest, sorce) 把sorce字符串添加到dest字符串的后面

LOGI("END");

return (*env)->NewStringUTF(env, strcat(p,newstr)); }

//处理数组中的每一个元素

JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod (JNIEnv * env, jobject obj, jintArray arr){

// 1.获取到 arr的大小

int len = (*env)->GetArrayLength(env, arr);

LOGI("len=%d", len);

if(len==0){

return arr;

}

//取出数组中第一个元素的内存地址

jint* p = (*env)-> GetIntArrayElements(env,arr,0);

int i=0;

for(;i<len;i++){

LOGI("len=%ld", *(p+i));//取出的每个元素

*(p+i) += 5; //取出的每个元素加五

}

return arr;

}
//引入头文件

#include "cn_itcast_ndk3_DataProvider.h"

#include <string.h> //导入日志头文件

#include <android/log.h> //修改日志tag中的值

#define LOG_TAG "logfromc" //日志显示的等级

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) // java中的jstring, 转化为c的一个字符数组

char* Jstring2CStr(JNIEnv* env, jstring jstr) {

char* rtn = NULL;

jclass clsstring = (*env)->FindClass(env,"java/lang/String");

jstring strencode = (*env)->NewStringUTF(env,"GB2312");

jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");

jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");

jsize alen = (*env)->GetArrayLength(env,barr);

jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);

if(alen > 0) {

rtn = (char*)malloc(alen+1); //new char[alen+1]; "\0"

memcpy(rtn,ba,alen);

rtn[alen]=0;

}

(*env)->ReleaseByteArrayElements(env,barr,ba,0); //释放内存

return rtn;

}

//处理整形相加

JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add (JNIEnv * env, jobject obj, jint x, jint y){

//打印 java 传递过来的 jstring ;

LOGI("log from c code ");

LOGI("x= %ld",x);

LOGD("y= %ld",y);

return x+y;

}

//处理字符串追加

JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC (JNIEnv * env, jobject obj, jstring str)

{

char* p = Jstring2CStr(env,str);

LOGI("%s",p);

char* newstr = "append string"; //strcat(dest, sorce) 把sorce字符串添加到dest字符串的后面

LOGI("END");

return (*env)->NewStringUTF(env, strcat(p,newstr));

}

//处理数组中的每一个元素

JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod (JNIEnv * env, jobject obj, jintArray arr){

// 1.获取到 arr的大小

int len = (*env)->GetArrayLength(env, arr);

LOGI("len=%d", len);

if(len==0){ return arr; }

//取出数组中第一个元素的内存地址

jint* p = (*env)-> GetIntArrayElements(env,arr,0);

int i=0;

for(;i<len;i++){

LOGI("len=%ld", *(p+i));//取出的每个元素

*(p+i) += 5; //取出的每个元素加五

}

return arr;

}
编写Android.mk文件

view plainprint?

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := Hello

LOCAL_SRC_FILES := Hello.c #增加 log 函数对应的log 库 liblog.so libthread_db.a

LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := Hello

LOCAL_SRC_FILES := Hello.c

#增加 log 函数对应的log 库 liblog.so libthread_db.a

LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)
Java代码load 动态库.调用native代码

view plainprint?

static{

System.loadLibrary("Hello");

}

DataProvider dp;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

dp = new DataProvider(); }

//add对应的事件

public void add(View view){

//执行C语言处理数据

int result = dp.add(3, 5);

Toast.makeText(this, "相加的结果"+ result, 1).show();

}
static{ System.loadLibrary("Hello");

}

DataProvider dp;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

dp = new DataProvider();

}

//add对应的事件

public void add(View view){

//执行C语言处理数据

int result = dp.add(3, 5);

Toast.makeText(this, "相加的结果"+ result, 1).show();

}

C中回调java方法

声明native 方法:

view plainprint?

public class DataProvider{

public native void callCcode();

public native void callCcode1();

public native void callCcode2(); ///C调用java中的空方法

public void helloFromJava(){

System.out.println("hello from java ");

}

//C调用java中的带两个int参数的方法

public int Add(int x,int y){

System.out.println("相加的结果为"+ (x+y));

return x+y;

}

//C调用java中参数为string的方法

public void printString(String s){

System.out.println("in java code "+ s);

}

}
public class DataProvider{

public native void callCcode();

public native void callCcode1();

public native void callCcode2();

///C调用java中的空方法

public void helloFromJava(){ System.out.println("hello from java ");

}

//C调用java中的带两个int参数的方法

public int Add(int x,int y){

System.out.println("相加的结果为"+ (x+y)); return x+y;

}

//C调用java中参数为string的方法

public void printString(String s){ System.out.println("in java code "+ s); }

}
头文件可以用jdk自带的javah进行自动生成,使用javap -s可以获取到方法的签名。

C代码实现回调需要三个步骤:首先要要获取到 某个对象 , 然后获取对象里面的方法 ,最后 调用这个方法 .

view plainprint?

#include "cn_itcast_ndk4_DataProvider.h"

#include <string.h>

#include <android/log.h>

#define LOG_TAG "logfromc"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

//1.调用java中的无参helloFromJava方法

JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode (JNIEnv * env , jobject obj){

// 获取到DataProvider对象

char* classname = "cn/itcast/ndk4/DataProvider";

jclass dpclazz = (*env)->FindClass(env,classname);

if (dpclazz == 0) {

LOGI("not find class!");

} else

LOGI("find class");

//第三个参数 和第四个参数 是方法的签名,第三个参数是方法名 , 第四个参数是根据返回值和参数生成的 //获取到DataProvider要调用的方法

jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"helloFromJava","()V");

if (methodID == 0) {

LOGI("not find method!");

}

else LOGI("find method");

//调用这个方法

(*env)->CallVoidMethod(env, obj,methodID);

}

// 2.调用java中的printString方法传递一个字符串

JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode1 (JNIEnv * env, jobject obj){ LOGI("in code");

// 获取到DataProvider对象

char* classname = "cn/itcast/ndk4/DataProvider";

jclass dpclazz = (*env)->FindClass(env,classname);

if (dpclazz == 0) {

LOGI("not find class!");

} else

LOGI("find class");

// 获取到要调用的method

jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"printString","(Ljava/lang/String;)V");

if (methodID == 0) {

LOGI("not find method!");

}

else

LOGI("find method");

//调用这个方法

(*env)->CallVoidMethod(env, obj,methodID,(*env)->NewStringUTF(env,"haha"));

}

// 3. 调用java中的add方法 , 传递两个参数 jint x,y

JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode2 (JNIEnv * env, jobject obj){

char* classname = "cn/itcast/ndk4/DataProvider";

jclass dpclazz = (*env)->FindClass(env,classname);

jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"Add","(II)I");

(*env)->CallIntMethod(env, obj,methodID,3l,4l);

}
#include "cn_itcast_ndk4_DataProvider.h"

#include <string.h>

#include <android/log.h>

#define LOG_TAG "logfromc"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

//1.调用java中的无参helloFromJava方法

JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode (JNIEnv * env , jobject obj){

// 获取到DataProvider对象

char* classname = "cn/itcast/ndk4/DataProvider";

jclass dpclazz = (*env)->FindClass(env,classname);

if (dpclazz == 0) {

LOGI("not find class!");

} else LOGI("find class");

//第三个参数 和第四个参数 是方法的签名,第三个参数是方法名 , 第四个参数是根据返回值和参数生成的 //获取到DataProvider要调用的方法

jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"helloFromJava","()V");

if (methodID == 0)

{

LOGI("not find method!");

}

else

LOGI("find method");

//调用这个方法

(*env)->CallVoidMethod(env, obj,methodID); }

// 2.调用java中的printString方法传递一个字符串

JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode1 (JNIEnv * env, jobject obj){ LOGI("in code");

// 获取到DataProvider对象

char* classname = "cn/itcast/ndk4/DataProvider";

jclass dpclazz = (*env)->FindClass(env,classname);

if (dpclazz == 0)

{

LOGI("not find class!");

}

else LOGI("find class");

// 获取到要调用的method

jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"printString","(Ljava/lang/String;)V");

if (methodID == 0) {

LOGI("not find method!");

}

else LOGI("find method");

//调用这个方法

(*env)->CallVoidMethod(env, obj,methodID,(*env)->NewStringUTF(env,"haha")); } // 3. 调用java中的add方法 , 传递两个参数

jint x,y JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode2 (JNIEnv * env, jobject obj){

char* classname = "cn/itcast/ndk4/DataProvider";

jclass dpclazz = (*env)->FindClass(env,classname);

jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"Add","(II)I");

(*env)->CallIntMethod(env, obj,methodID,3l,4l);

}

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