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

来源:互联网 发布:网络广告自动发布软件 编辑:程序博客网 时间:2024/04/27 15:13

介绍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(35);  
       
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);

}


原创粉丝点击