• 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 方法:

    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);   
    11. }  

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

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

    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. }  
    36. #endif  
    37. #endif  

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

    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. {  
    15.      char*   rtn   =   NULL;  
    16.      jclass   clsstring   =   (*env)->FindClass(env,"java/lang/String");  
    17.      jstring   strencode   =   (*env)->NewStringUTF(env,"GB2312");  
    18.      jmethodID   mid   =   (*env)->GetMethodID(env,clsstring,   "getBytes",   "(Ljava/lang/String;)[B");  
    19.      jbyteArray   barr=   (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");  
    20.      jsize   alen   =   (*env)->GetArrayLength(env,barr);  
    21.      jbyte*   ba   =   (*env)->GetByteArrayElements(env,barr,JNI_FALSE);  
    22.      if(alen   >   0)  
    23.      {  
    24.       rtn   =   (char*)malloc(alen+1);         //new   char[alen+1]; ""  
    25.       memcpy(rtn,ba,alen);  
    26.       rtn[alen]=0;  
    27.      }  
    28.      (*env)->ReleaseByteArrayElements(env,barr,ba,0);  //释放内存  
    29.   
    30.      return rtn;  
    31. }  
    32.   
    33. //处理整形相加  
    34. JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add  
    35.   (JNIEnv * env, jobject obj, jint x, jint y){  
    36.     //打印 java 传递过来的 jstring ;  
    37.     LOGI("log from c code ");  
    38.     LOGI("x= %ld",x);  
    39.     LOGD("y= %ld",y);  
    40.     return x+y;  
    41. }  
    42.   
    43. //处理字符串追加  
    44. JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC  
    45.   (JNIEnv * env, jobject obj, jstring str){  
    46.   
    47.     char* p =  Jstring2CStr(env,str);  
    48.     LOGI("%s",p);  
    49.     char* newstr = "append string";  
    50.   
    51.     //strcat(dest, sorce) 把sorce字符串添加到dest字符串的后面  
    52.     LOGI("END");  
    53.     return (*env)->NewStringUTF(env, strcat(p,newstr));  
    54. }  
    55.   
    56. //处理数组中的每一个元素  
    57. JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod  
    58.   (JNIEnv * env, jobject obj, jintArray arr){  
    59.     // 1.获取到 arr的大小  
    60.   
    61.     int len = (*env)->GetArrayLength(env, arr);  
    62.     LOGI("len=%d", len);  
    63.   
    64.     if(len==0){  
    65.         return arr;  
    66.     }  
    67.     //取出数组中第一个元素的内存地址  
    68.     jint* p = (*env)-> GetIntArrayElements(env,arr,0);  
    69.     int i=0;  
    70.     for(;i<len;i++){  
    71.         LOGI("len=%ld", *(p+i));//取出的每个元素  
    72.         *(p+i) += 5; //取出的每个元素加五  
    73.     }  
    74.     return arr;  
    75. }  

    编写Android.mk文件

    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)  

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

    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.     }  

    C中回调java方法

    声明native 方法:

    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.     }  
    19. }  

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

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

    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.   
    29. // 2.调用java中的printString方法传递一个字符串  
    30. JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode1  
    31.   (JNIEnv * env, jobject obj){  
    32.     LOGI("in code");  
    33.     // 获取到DataProvider对象  
    34.     char* classname = "cn/itcast/ndk4/DataProvider";  
    35.     jclass dpclazz = (*env)->FindClass(env,classname);  
    36.     if (dpclazz == 0) {  
    37.             LOGI("not find class!");  
    38.         } else  
    39.             LOGI("find class");  
    40.     // 获取到要调用的method  
    41.     jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"printString","(Ljava/lang/String;)V");  
    42.     if (methodID == 0) {  
    43.             LOGI("not find method!");  
    44.         } else  
    45.             LOGI("find method");  
    46.   
    47.     //调用这个方法  
    48.     (*env)->CallVoidMethod(env, obj,methodID,(*env)->NewStringUTF(env,"haha"));  
    49. }  
    50.   
    51. // 3. 调用java中的add方法 , 传递两个参数 jint x,y  
    52. JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode2  
    53.   (JNIEnv * env, jobject obj){  
    54.     char* classname = "cn/itcast/ndk4/DataProvider";  
    55.     jclass dpclazz = (*env)->FindClass(env,classname);  
    56.     jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"Add","(II)I");  
    57.     (*env)->CallIntMethod(env, obj,methodID,3l,4l);  
    58. }  

     

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

  • 相关阅读:
    Docker安装及基本命令
    SpringBoot-获取YAML文件值
    SpringBoot-YAML语法
    Maven仓库
    第一次使用Maven
    初始Maven
    索引的基本使用与创建选择
    了解索引为什么能快速查找数据
    Mysql的执行顺序与优化分析
    Mysql常用的几种join连接方式
  • 原文地址:https://www.cnblogs.com/YangBinChina/p/4252038.html
Copyright © 2020-2023  润新知