Android NDK開發(四)——Java傳遞資料到C

來源:互聯網
上載者:User

標籤:jni   ndk   java   c   javah   

        轉載請註明出處:http://blog.csdn.net/allen315410/article/details/41845701

        前面幾篇文章介紹了Android NDK開發的簡單概念、常見錯誤及處理和從第一個Hello World開始實際做一個簡單的JNI開發樣本,相信看完之後,大家對NDK開發有了一個概念上的認識了,那麼接下來我們需要再深入一下NDK的開發,我們知道NDK開發就是使用JNI這層“協議”在Java和C之間起個“橋樑”的作用,將Java和Native C之間聯立起來,讓Java和C直接的資料進行互調。談到Java和C之間的資料調用,那麼Java是怎樣傳遞資料到C中的呢,C拿到資料處理完後又怎樣將處理後的資料回傳給Java的呢?先別急,接下來我們就看看Java怎麼傳遞資料給C的。

1,建立一個Android工程,在工程下建立一個DataProvider類,在這個類裡定義3個native方法,如下:

package com.example.ndktransferdata;public class DataProvider {/** * 把兩個java中的int傳遞給C語言,c語言處理完畢後,把相加的結果返回給java *  * @param x * @param y * @return */public native int add(int x, int y);/** * 把java中的String傳遞給c語言,c語言擷取後,在string後面添加一個hello字串,返回給java *  * @param s * @return */public native String sayHelloInC(String s);/** * 把java中的一個int數組傳遞給C語言,C語言接收這個數組,把int數組中的每一個元素+10,然後返回給Java *  * @param iNum * @return */public native int[] intMethod(int[] iNum);}
2,用Javah編譯標頭檔

做到這一步發現了一個問題,從描述上看應該是編碼錯誤,這裡錯誤的使用了GBK來編譯java檔案了,改成UTF-8就沒問題,只要在javah命令後面添加 -encoding utf-8,為編譯器提供編碼環境就行,下面是改正後的結果:


說明native代碼的函數簽名已經產生了,我們將這個產生的函數簽名標頭檔剪下到jni目錄下,在c代碼中引用這個標頭檔就好了。

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_example_ndktransferdata_DataProvider */#ifndef _Included_com_example_ndktransferdata_DataProvider#define _Included_com_example_ndktransferdata_DataProvider#ifdef __cplusplusextern "C" {#endif/* * Class:     com_example_ndktransferdata_DataProvider * Method:    add * Signature: (II)I */JNIEXPORT jint JNICALL Java_com_example_ndktransferdata_DataProvider_add  (JNIEnv *, jobject, jint, jint);/* * Class:     com_example_ndktransferdata_DataProvider * Method:    sayHelloInC * Signature: (Ljava/lang/String;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_ndktransferdata_DataProvider_sayHelloInC  (JNIEnv *, jobject, jstring);/* * Class:     com_example_ndktransferdata_DataProvider * Method:    intMethod * Signature: ([I)[I */JNIEXPORT jintArray JNICALL Java_com_example_ndktransferdata_DataProvider_intMethod  (JNIEnv *, jobject, jintArray);#ifdef __cplusplus}#endif#endif

3,編寫C語言代碼

        之前在第二步的時候我們編譯好了函數簽名的標頭檔,所以這裡我們就需要用過標頭檔中的方法簽名了,一共包含3個這樣的native函數,函數裡實現是這樣的:

#include<stdio.h>#include<jni.h>#include<malloc.h>#include<string.h>#include"com_example_ndktransferdata_DataProvider.h"#include<android/log.h>#define LOG_TAG "System.out.c"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)/** * 傳回值 char* 這個代表char數組的首地址 *  Jstring2CStr 把java中的jstring的類型轉化成一個c語言中的char 字串 */char* Jstring2CStr(JNIEnv* env, jstring jstr) {char* rtn = NULL;jclass clsstring = (*env)->FindClass(env, "java/lang/String"); //Stringjstring strencode = (*env)->NewStringUTF(env, "GB2312"); // 得到一個java字串 "GB2312"jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes","(Ljava/lang/String;)[B"); //[ String.getBytes("gb2312");jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,strencode); // String .getByte("GB2312");jsize alen = (*env)->GetArrayLength(env, barr); // byte數組的長度jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);if (alen > 0) {rtn = (char*) malloc(alen + 1); //"\0"memcpy(rtn, ba, alen);rtn[alen] = 0;}(*env)->ReleaseByteArrayElements(env, barr, ba, 0); //return rtn;}JNIEXPORT jint JNICALL Java_com_example_ndktransferdata_DataProvider_add(JNIEnv * env, jobject obj, jint x, jint y) {LOGD("x = %d", x);LOGD("y = %d", y);return x + y;}JNIEXPORT jstring JNICALL Java_com_example_ndktransferdata_DataProvider_sayHelloInC(JNIEnv * env, jobject obj, jstring jstr) {char* cstr = Jstring2CStr(env, jstr);LOGD("cstr = %s", cstr);char arr[7] = { ' ', 'h', 'e', 'l', 'l', 'o', '\0' };strcat(cstr, arr);LOGD("new cstr = %s", cstr);return (*env)->NewStringUTF(env, cstr);}JNIEXPORT jintArray JNICALL Java_com_example_ndktransferdata_DataProvider_intMethod(JNIEnv * env, jobject obj, jintArray jarr) {//擷取傳遞進來數組的長度int len = (*env)->GetArrayLength(env, jarr);//擷取傳遞進來數組的元素,即數組首地址jint* intArr = (*env)->GetIntArrayElements(env, jarr, 0);int i = 0;for (; i < len; i++) {//列印處理前的數組元素LOGD("intArr[%d] = %d", i, intArr[i]);//遍曆數組元素+10*(intArr + i) += 10;}return jarr;}

4,配置Android.mk檔案

    LOCAL_PATH := $(call my-dir)    include $(CLEAR_VARS)    LOCAL_MODULE    := Hello    LOCAL_SRC_FILES := Hello.c        LOCAL_LDLIBS += -llog    include $(BUILD_SHARED_LIBRARY)
光配置Android.mk檔案大致就可以了,但是還需要解決一個版本相容問題,做法是在jni目錄下建立Application.mk檔案,加上

APP_PLATFORM := android-8

5,編譯C語言代碼

6,Java代碼中處理返回的資料

       Java中傳遞資料到C代碼中,C代碼處理完後返回給Java,這時候Java拿到資料後就可以做自己的一些業務操作了,首先我們編譯完Native代碼後,先Refresh一下工程,然後clean一下工程,編寫如下的測試案例:

public class MainActivity extends Activity implements OnClickListener {// 載入本地庫檔案static {System.loadLibrary("Hello");}private Button btn1, btn2, btn3;private DataProvider provider;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn1 = (Button) findViewById(R.id.btn1);btn2 = (Button) findViewById(R.id.btn2);btn3 = (Button) findViewById(R.id.btn3);btn1.setOnClickListener(this);btn2.setOnClickListener(this);btn3.setOnClickListener(this);provider = new DataProvider();}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn1 : // 傳遞2個int給C代碼int result = provider.add(3, 5);Toast.makeText(this, "相加的結果:" + result, 0).show();break;case R.id.btn2 : // 傳遞string給C代碼String str = provider.sayHelloInC("zhang san");Toast.makeText(this, str, 0).show();break;case R.id.btn3 : // 傳遞int數組給C代碼int[] arr = {1, 2, 3, 4, 5};provider.intMethod(arr);for (int i = 0; i < arr.length; i++) {System.out.println("arr[" + i + "] = " + arr[i]);}break;default :break;}}}
        運行一下工程,注意:這裡只能開啟arm模擬器,如果是x86模擬器會安裝apk時候報錯,因為這段native代碼只編寫了arm支援的版本,沒有支援x86。如果在運行測試的時候出現一些錯誤的話,請參考上篇文章中提示慢慢解決。Android NDK開發——常見錯誤集錦以及LOG使用

測試1:Java傳遞2個int給C


Logcat輸出:


測試2:Java傳遞string給C


Logcat輸出:


測試3:Java傳遞int數組給C

Logcat輸出:


總結:

        上述這個簡單的樣本可以說明ndk開發中,Java是怎樣將資料傳遞給C代碼的了,程式中只是簡單的介紹了3種資料類型int,string和int[],這是遠遠不夠的,因為Java支援的資料類型比較多,這時候怎麼辦?好,有了上面的例子,我們可以舉一反三了,在之前的部落格中我也強調過ndk解壓包下的jni.h這個檔案的重要性,這個檔案不僅僅定義了Java資料類型在C語言中的表示,看一下源碼,就發現一種一一映射的關係:

......typedef uint8_t         jboolean;       /* unsigned 8 bits */typedef int8_t          jbyte;          /* signed 8 bits */typedef uint16_t        jchar;          /* unsigned 16 bits */typedef int16_t         jshort;         /* signed 16 bits */typedef int32_t         jint;           /* signed 32 bits */typedef int64_t         jlong;          /* signed 64 bits */typedef float           jfloat;         /* 32-bit IEEE 754 */typedef double          jdouble;        /* 64-bit IEEE 754 */#elsetypedef unsigned char   jboolean;       /* unsigned 8 bits */typedef signed char     jbyte;          /* signed 8 bits */typedef unsigned short  jchar;          /* unsigned 16 bits */typedef short           jshort;         /* signed 16 bits */typedef int             jint;           /* signed 32 bits */typedef long long       jlong;          /* signed 64 bits */typedef float           jfloat;         /* 32-bit IEEE 754 */typedef double          jdouble;        /* 64-bit IEEE 754 */......
另外還有一個非常重要的結構體JNINativeInterface,這裡面定義了很多C函數,只要看懂大致意思就可以試著去調用這些函數,這些函數在native開發中顯得特別重要:

......    jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);    jbyteArray    (*NewByteArray)(JNIEnv*, jsize);    jcharArray    (*NewCharArray)(JNIEnv*, jsize);    jshortArray   (*NewShortArray)(JNIEnv*, jsize);    jintArray     (*NewIntArray)(JNIEnv*, jsize);    jlongArray    (*NewLongArray)(JNIEnv*, jsize);    jfloatArray   (*NewFloatArray)(JNIEnv*, jsize);    jdoubleArray  (*NewDoubleArray)(JNIEnv*, jsize);    jboolean*   (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);    jbyte*      (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);    jchar*      (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);    jshort*     (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);    jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);    jlong*      (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);    jfloat*     (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);    jdouble*    (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);......
源碼比較長,有興趣的朋友自己翻看一下,這裡只貼部分。


源碼請在這裡下載


Android NDK開發(四)——Java傳遞資料到C

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.