介紹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 方法:
public class DataProvider {// 兩個java中的int 傳遞c 語言 , c語言處理這個相加的邏輯,把相加的結果返回給javapublic native int add(int x ,int y);//把一個java中的字串傳遞給c語言, c 語言處理下字串, 處理完畢返回給java public native String sayHelloInC(String s);//把一個java中int類型的數組傳遞給c語言, c語言裡面把數組的每一個元素的值 都增加5, //然後在把處理完畢的數組,返回給javapublic native int[] intMethod(int[] iNum); }
以上方法要在C中實現的標頭檔,標頭檔可以理解為要在C中實現的方法
其中 JENEnv* 代表的是java環境 , 通過這個環境可以調用java的方法,jobject 表示哪個對象調用了 這個c語言的方法, thiz就表示的是當前的對象
/* 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 __cplusplusextern "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 中進行調試
//引入標頭檔#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檔案
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := HelloLOCAL_SRC_FILES := Hello.c #增加 log 函數對應的log 庫 liblog.so libthread_db.aLOCAL_LDLIBS += -lloginclude $(BUILD_SHARED_LIBRARY)
Java代碼load 動態庫.調用native代碼
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 方法:
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代碼實現回調需要三個步驟:首先要要擷取到 某個對象 , 然後擷取對象裡面的方法 ,最後 調用這個方法 .
#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!");} elseLOGI("find class");//第三個參數 和第四個參數 是方法的簽名,第三個參數是方法名 , 第四個參數是根據傳回值和參數產生的//擷取到DataProvider要調用的方法jmethodID methodID = (*env)->GetMethodID(env,dpclazz,"helloFromJava","()V");if (methodID == 0) {LOGI("not find method!");} elseLOGI("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!");} elseLOGI("find class");// 擷取到要調用的methodjmethodID methodID = (*env)->GetMethodID(env,dpclazz,"printString","(Ljava/lang/String;)V");if (methodID == 0) {LOGI("not find method!");} elseLOGI("find method");//調用這個方法(*env)->CallVoidMethod(env, obj,methodID,(*env)->NewStringUTF(env,"haha"));}// 3. 調用java中的add方法 , 傳遞兩個參數 jint x,yJNIEXPORT 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);}