【郭林专刊】JNI实现Java与C之间传递数据 ==============android

本文介绍Java如何通过JNI与C语言进行数据交换及方法回调。包括基本数据类型、字符串和数组的传递,以及从C语言回调Java方法的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

  1. publicclassDataProvider{
  2. //两个java中的int传递c语言,c语言处理这个相加的逻辑,把相加的结果返回给java
  3. publicnativeintadd(intx,inty);
  4. //把一个java中的字符串传递给c语言,c语言处理下字符串,处理完毕返回给java
  5. publicnativeStringsayHelloInC(Strings);
  6. //把一个java中int类型的数组传递给c语言,c语言里面把数组的每一个元素的值都增加5,
  7. //然后在把处理完毕的数组,返回给java
  8. publicnativeint[]intMethod(int[]iNum);
  9. }
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就表示的是当前的对象

  1. /*DONOTEDITTHISFILE-itismachinegenerated*/
  2. #include<jni.h>
  3. /*Headerforclasscn_itcast_ndk3_DataProvider*/
  4. #ifndef_Included_cn_itcast_ndk3_DataProvider
  5. #define_Included_cn_itcast_ndk3_DataProvider
  6. #ifdef__cplusplus
  7. extern"C"{
  8. #endif
  9. /*
  10. *Class:cn_itcast_ndk3_DataProvider
  11. *Method:add
  12. *Signature:(II)I
  13. */
  14. JNIEXPORTjintJNICALLJava_cn_itcast_ndk3_DataProvider_add
  15. (JNIEnv*,jobject,jint,jint);
  16. /*
  17. *Class:cn_itcast_ndk3_DataProvider
  18. *Method:sayHelloInC
  19. *Signature:(Ljava/lang/String;)Ljava/lang/String;
  20. */
  21. JNIEXPORTjstringJNICALLJava_cn_itcast_ndk3_DataProvider_sayHelloInC
  22. (JNIEnv*,jobject,jstring);
  23. /*
  24. *Class:cn_itcast_ndk3_DataProvider
  25. *Method:intMethod
  26. *Signature:([I)[I
  27. */
  28. JNIEXPORTjintArrayJNICALLJava_cn_itcast_ndk3_DataProvider_intMethod
  29. (JNIEnv*,jobject,jintArray);
  30. #ifdef__cplusplus
  31. }
  32. #endif
  33. #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 中进行调试

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

  1. LOCAL_PATH:=$(callmy-dir)
  2. include$(CLEAR_VARS)
  3. LOCAL_MODULE:=Hello
  4. LOCAL_SRC_FILES:=Hello.c
  5. #增加log函数对应的log库liblog.solibthread_db.a
  6. LOCAL_LDLIBS+=-llog
  7. 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代码

  1. static{
  2. System.loadLibrary("Hello");
  3. }
  4. DataProviderdp;
  5. @Override
  6. publicvoidonCreate(BundlesavedInstanceState){
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. dp=newDataProvider();
  10. }
  11. //add对应的事件
  12. publicvoidadd(Viewview){
  13. //执行C语言处理数据
  14. intresult=dp.add(3,5);
  15. Toast.makeText(this,"相加的结果"+result,1).show();
  16. }
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 方法:

  1. publicclassDataProvider{
  2. publicnativevoidcallCcode();
  3. publicnativevoidcallCcode1();
  4. publicnativevoidcallCcode2();
  5. ///C调用java中的空方法
  6. publicvoidhelloFromJava(){
  7. System.out.println("hellofromjava");
  8. }
  9. //C调用java中的带两个int参数的方法
  10. publicintAdd(intx,inty){
  11. System.out.println("相加的结果为"+(x+y));
  12. returnx+y;
  13. }
  14. //C调用java中参数为string的方法
  15. publicvoidprintString(Strings){
  16. System.out.println("injavacode"+s);
  17. }
  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代码实现回调需要三个步骤:首先要要获取到 某个对象 , 然后获取对象里面的方法 ,最后 调用这个方法 .

  1. #include"cn_itcast_ndk4_DataProvider.h"
  2. #include<string.h>
  3. #include<android/log.h>
  4. #defineLOG_TAG"logfromc"
  5. #defineLOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
  6. #defineLOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
  7. //1.调用java中的无参helloFromJava方法
  8. JNIEXPORTvoidJNICALLJava_cn_itcast_ndk4_DataProvider_callCcode
  9. (JNIEnv*env,jobjectobj){
  10. //获取到DataProvider对象
  11. char*classname="cn/itcast/ndk4/DataProvider";
  12. jclassdpclazz=(*env)->FindClass(env,classname);
  13. if(dpclazz==0){
  14. LOGI("notfindclass!");
  15. }else
  16. LOGI("findclass");
  17. //第三个参数和第四个参数是方法的签名,第三个参数是方法名,第四个参数是根据返回值和参数生成的
  18. //获取到DataProvider要调用的方法
  19. jmethodIDmethodID=(*env)->GetMethodID(env,dpclazz,"helloFromJava","()V");
  20. if(methodID==0){
  21. LOGI("notfindmethod!");
  22. }else
  23. LOGI("findmethod");
  24. //调用这个方法
  25. (*env)->CallVoidMethod(env,obj,methodID);
  26. }
  27. //2.调用java中的printString方法传递一个字符串
  28. JNIEXPORTvoidJNICALLJava_cn_itcast_ndk4_DataProvider_callCcode1
  29. (JNIEnv*env,jobjectobj){
  30. LOGI("incode");
  31. //获取到DataProvider对象
  32. char*classname="cn/itcast/ndk4/DataProvider";
  33. jclassdpclazz=(*env)->FindClass(env,classname);
  34. if(dpclazz==0){
  35. LOGI("notfindclass!");
  36. }else
  37. LOGI("findclass");
  38. //获取到要调用的method
  39. jmethodIDmethodID=(*env)->GetMethodID(env,dpclazz,"printString","(Ljava/lang/String;)V");
  40. if(methodID==0){
  41. LOGI("notfindmethod!");
  42. }else
  43. LOGI("findmethod");
  44. //调用这个方法
  45. (*env)->CallVoidMethod(env,obj,methodID,(*env)->NewStringUTF(env,"haha"));
  46. }
  47. //3.调用java中的add方法,传递两个参数jintx,y
  48. JNIEXPORTvoidJNICALLJava_cn_itcast_ndk4_DataProvider_callCcode2
  49. (JNIEnv*env,jobjectobj){
  50. char*classname="cn/itcast/ndk4/DataProvider";
  51. jclassdpclazz=(*env)->FindClass(env,classname);
  52. jmethodIDmethodID=(*env)->GetMethodID(env,dpclazz,"Add","(II)I");
  53. (*env)->CallIntMethod(env,obj,methodID,3l,4l);
  54. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值