欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

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

来源: javaer 分享于  点击 9009 次 点评:280

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


http://blog.csdn.net/furongkang/article/details/6857610

 

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

 

介绍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 方法:

[java] view plaincopyprint?
  1. public class DataProvider { 
  2.     // 两个java中的int 传递c 语言 ,  c语言处理这个相加的逻辑,把相加的结果返回给java 
  3.     public native int add(int x ,int y); 
  4.      
  5.     //把一个java中的字符串传递给c语言, c 语言处理下字符串, 处理完毕返回给java  
  6.     public native String sayHelloInC(String s); 
  7.  
  8.     //把一个java中int类型的数组传递给c语言, c语言里面把数组的每一个元素的值 都增加5,  
  9.     //然后在把处理完毕的数组,返回给java 
  10.     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就表示的是当前的对象

[cpp] view plaincopyprint?
  1. /* DO NOT EDIT THIS FILE - it is machine generated */ 
  2. #include <jni.h> 
  3. /* Header for class cn_itcast_ndk3_DataProvider */ 
  4.  
  5. #ifndef _Included_cn_itcast_ndk3_DataProvider 
  6. #define _Included_cn_itcast_ndk3_DataProvider 
  7. #ifdef __cplusplus 
  8. extern "C"
  9. #endif 
  10. /*
  11. * Class:     cn_itcast_ndk3_DataProvider
  12. * Method:    add
  13. * Signature: (II)I
  14. */ 
  15. JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add 
  16.   (JNIEnv *, jobject, jint, jint); 
  17.  
  18. /*
  19. * Class:     cn_itcast_ndk3_DataProvider
  20. * Method:    sayHelloInC
  21. * Signature: (Ljava/lang/String;)Ljava/lang/String;
  22. */ 
  23. JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC 
  24.   (JNIEnv *, jobject, jstring); 
  25.  
  26. /*
  27. * Class:     cn_itcast_ndk3_DataProvider
  28. * Method:    intMethod
  29. * Signature: ([I)[I
  30. */ 
  31. JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod 
  32.   (JNIEnv *, jobject, jintArray); 
  33.  
  34. #ifdef __cplusplus 
  35. #endif 
  36. #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 中进行调试

[cpp] view plaincopyprint?
  1. //引入头文件 
  2. #include "cn_itcast_ndk3_DataProvider.h" 
  3. #include <string.h> 
  4. //导入日志头文件 
  5. #include <android/log.h> 
  6. //修改日志tag中的值 
  7. #define LOG_TAG "logfromc" 
  8. //日志显示的等级 
  9. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 
  10. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 
  11.  
  12. // java中的jstring, 转化为c的一个字符数组 
  13. char*   Jstring2CStr(JNIEnv*   env,   jstring   jstr) 
  14.      char*   rtn   =   NULL; 
  15.      jclass   clsstring   =   (*env)->FindClass(env,"java/lang/String"); 
  16.      jstring   strencode   =   (*env)->NewStringUTF(env,"GB2312"); 
  17.      jmethodID   mid   =   (*env)->GetMethodID(env,clsstring,   "getBytes",   "(Ljava/lang/String;)[B"); 
  18.      jbyteArray   barr=   (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312"); 
  19.      jsize   alen   =   (*env)->GetArrayLength(env,barr); 
  20.      jbyte*   ba   =   (*env)->GetByteArrayElements(env,barr,JNI_FALSE); 
  21.      if(alen   >   0) 
  22.      { 
  23.       rtn   =   (char*)malloc(alen+1);         //new   char[alen+1]; "\0" 
  24.       memcpy(rtn,ba,alen); 
  25.       rtn[alen]=0; 
  26.      } 
  27.      (*env)->ReleaseByteArrayElements(env,barr,ba,0);  //释放内存 
  28.  
  29.      return rtn; 
  30.  
  31. //处理整形相加 
  32. JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add 
  33.   (JNIEnv * env, jobject obj, jint x, jint y){ 
  34.     //打印 java 传递过来的 jstring ; 
  35.     LOGI("log from c code "); 
  36.     LOGI("x= %ld",x); 
  37.     LOGD("y= %ld",y); 
  38.     return x+y; 
  39.  
  40. //处理字符串追加 
  41. JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC 
  42.   (JNIEnv * env, jobject obj, jstring str){ 
  43.  
  44.     char* p =  Jstring2CStr(env,str); 
  45.     LOGI("%s",p); 
  46.     char* newstr = "append string"
  47.  
  48.     //strcat(dest, sorce) 把sorce字符串添加到dest字符串的后面 
  49.     LOGI("END"); 
  50.     return (*env)->NewStringUTF(env, strcat(p,newstr)); 
  51.  
  52. //处理数组中的每一个元素 
  53. JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod 
  54.   (JNIEnv * env, jobject obj, jintArray arr){ 
  55.     // 1.获取到 arr的大小 
  56.  
  57.     int len = (*env)->GetArrayLength(env, arr); 
  58.     LOGI("len=%d", len); 
  59.  
  60.     if(len==0){ 
  61.         return arr; 
  62.     } 
  63.     //取出数组中第一个元素的内存地址 
  64.     jint* p = (*env)-> GetIntArrayElements(env,arr,0); 
  65.     int i=0; 
  66.     for(;i<len;i++){ 
  67.         LOGI("len=%ld", *(p+i));//取出的每个元素 
  68.         *(p+i) += 5; //取出的每个元素加五 
  69.     } 
  70.     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文件

[cpp] view plaincopyprint?
  1. LOCAL_PATH := $(call my-dir) 
  2.  
  3. include $(CLEAR_VARS) 
  4.  
  5. LOCAL_MODULE    := Hello 
  6. LOCAL_SRC_FILES := Hello.c  
  7. #增加 log 函数对应的log 库  liblog.so  libthread_db.a 
  8. LOCAL_LDLIBS += -llog 
  9.  
  10. 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代码

[java] view plaincopyprint?
  1. static
  2.         System.loadLibrary("Hello"); 
  3.     } 
  4.     DataProvider dp; 
  5.      
  6.     @Override 
  7.     public void onCreate(Bundle savedInstanceState) { 
  8.         super.onCreate(savedInstanceState); 
  9.         setContentView(R.layout.main); 
  10.         dp = new DataProvider(); 
  11.     } 
  12.      
  13.     //add对应的事件 
  14.     public void add(View view){ 
  15.         //执行C语言处理数据 
  16.         int result = dp.add(3, 5); 
  17.         Toast.makeText(this, "相加的结果"+ result, 1).show();     
  18.     } 
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 方法:

[java] view plaincopyprint?
  1. public class DataProvider{ 
  2.     public native void callCcode(); 
  3.     public native void callCcode1(); 
  4.     public native void callCcode2(); 
  5.      
  6.     ///C调用java中的空方法   
  7.     public void helloFromJava(){ 
  8.         System.out.println("hello from java "); 
  9.     } 
  10.     //C调用java中的带两个int参数的方法 
  11.     public int Add(int x,int y){ 
  12.         System.out.println("相加的结果为"+ (x+y)); 
  13.         return x+y; 
  14.     } 
  15.     //C调用java中参数为string的方法 
  16.     public void printString(String s){ 
  17.         System.out.println("in java code "+ s); 
  18.     } 
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代码实现回调需要三个步骤:首先要要获取到 某个对象 , 然后获取对象里面的方法  ,最后 调用这个方法  .

[cpp] view plaincopyprint?
  1. #include "cn_itcast_ndk4_DataProvider.h" 
  2. #include <string.h> 
  3. #include <android/log.h> 
  4. #define LOG_TAG "logfromc" 
  5. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 
  6. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 
  7.  
  8. //1.调用java中的无参helloFromJava方法 
  9. JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode 
  10.   (JNIEnv * env , jobject obj){ 
  11.     // 获取到DataProvider对象 
  12.     char* classname = "cn/itcast/ndk4/DataProvider"
  13.     jclass dpclazz = (*env)->FindClass(env,classname); 
  14.     if (dpclazz == 0) { 
  15.             LOGI("not find class!"); 
  16.         } else 
  17.             LOGI("find class"); 
  18.     //第三个参数 和第四个参数 是方法的签名,第三个参数是方法名  , 第四个参数是根据返回值和参数生成的 
  19.     //获取到DataProvider要调用的方法 
  20.     jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"helloFromJava","()V"); 
  21.     if (methodID == 0) { 
  22.             LOGI("not find method!"); 
  23.         } else 
  24.             LOGI("find method"); 
  25.     //调用这个方法 
  26.     (*env)->CallVoidMethod(env, obj,methodID); 
  27.  
  28. // 2.调用java中的printString方法传递一个字符串 
  29. JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode1 
  30.   (JNIEnv * env, jobject obj){ 
  31.     LOGI("in code"); 
  32.     // 获取到DataProvider对象 
  33.     char* classname = "cn/itcast/ndk4/DataProvider"
  34.     jclass dpclazz = (*env)->FindClass(env,classname); 
  35.     if (dpclazz == 0) { 
  36.             LOGI("not find class!"); 
  37.         } else 
  38.             LOGI("find class"); 
  39.     // 获取到要调用的method 
  40.     jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"printString","(Ljava/lang/String;)V"); 
  41.     if (methodID == 0) { 
  42.             LOGI("not find method!"); 
  43.         } else 
  44.             LOGI("find method"); 
  45.  
  46.     //调用这个方法 
  47.     (*env)->CallVoidMethod(env, obj,methodID,(*env)->NewStringUTF(env,"haha")); 
  48.  
  49. // 3. 调用java中的add方法 , 传递两个参数 jint x,y 
  50. JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode2 
  51.   (JNIEnv * env, jobject obj){ 
  52.     char* classname = "cn/itcast/ndk4/DataProvider"
  53.     jclass dpclazz = (*env)->FindClass(env,classname); 
  54.     jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"Add","(II)I"); 
  55.     (*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);
}



 

相关文章

    暂无相关文章
相关栏目:

用户点评