知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們在追求其視覺表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營銷的便利,運(yùn)營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏壧峁┍憬莸闹С郑?
AndroidNDK開發(fā)(四)——Java傳遞數(shù)據(jù)到C
發(fā)表時(shí)間:2020-10-19
發(fā)布人:葵宇科技
瀏覽次數(shù):43
轉(zhuǎn)載請注明出處:http://blog.csdn.net/allen315410/article/details/41845701
前面幾篇文┞仿介紹了Android NDK開辟的簡單概念、常見缺點(diǎn)及處理和大年夜第一個(gè)Hello World開端實(shí)際做一個(gè)簡單的JNI開辟示例,信賴看完之后,大年夜家對NDK開辟有了一個(gè)概念上的熟悉了,那么接下來我們須要再深刻一下NDK的開辟,我們知道NDK開辟就是應(yīng)用JNI這層“協(xié)定”在Java和C之間起個(gè)“橋梁”的感化,將Java和Native C之間聯(lián)立起來,讓Java和C直接的數(shù)據(jù)進(jìn)行互調(diào)。談到Java和C之間的數(shù)據(jù)調(diào)用,那么Java是如何傳遞數(shù)據(jù)到C中的呢,C拿到數(shù)據(jù)處理完后又如何將處理后的數(shù)據(jù)回傳給Java的呢?先別急,接下來我們就看看Java怎么傳遞數(shù)據(jù)給C的。
1,建立一個(gè)Android工程,在工程下建立一個(gè)DataProvider類,在這個(gè)類里定義3個(gè)native辦法,如下:
package com.example.ndktransferdata; public class DataProvider { /** * 把兩個(gè)java中的int傳遞給C說話,c說話處理完畢后,把相加的結(jié)不雅返回給java * * @param x * @param y * @return */ public native int add(int x, int y); /** * 把java中的String傳遞給c說話,c說話獲取后,在string后面添加一個(gè)hello字符串,返回給java * * @param s * @return */ public native String sayHelloInC(String s); /** * 把java中的一個(gè)int數(shù)組傳遞給C說話,C說話接收這個(gè)數(shù)組,把int數(shù)組中的每一個(gè)元素+10,然后返回給Java * * @param iNum * @return */ public native int[] intMethod(int[] iNum); }
2,用Javah編譯頭文件
[img]http://img.blog.csdn.net/20141210144650625
做到這一步發(fā)清楚明了一個(gè)問題,大年夜描述上看竽暌功該是編碼缺點(diǎn),這里缺點(diǎn)的應(yīng)用了GBK來編譯java文件了,改成UTF-8就沒問題,只要在javah敕令后面添加 -encoding utf-8,為編譯器供給編碼情況就行,下面是改┞俘后的結(jié)不雅:
[img]http://img.blog.csdn.net/20141210145007368
解釋native代碼的函數(shù)簽名已經(jīng)生成了,我們將這個(gè)生成的函數(shù)簽名頭文件剪切到j(luò)ni目次下,在c代碼中引用這個(gè)頭文件就好了。
/* 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 __cplusplus extern "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說話代碼
之前在第二步的時(shí)刻我們編譯好了函數(shù)簽名的頭文件,所以這里我們就須要用過火文件中的辦法簽名了,一共包含3個(gè)如許的native函數(shù),函數(shù)誠實(shí)現(xiàn)是如許的:
#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* 這個(gè)代表char數(shù)組的首地址 * Jstring2CStr 把java中的jstring的類型轉(zhuǎn)化成一個(gè)c說話中的char 字符串 */ char* Jstring2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = (*env)->FindClass(env, "java/lang/String"); //String jstring strencode = (*env)->NewStringUTF(env, "GB2312"); // 獲得一個(gè)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數(shù)組的長度 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) { //獲取傳遞進(jìn)來數(shù)組的長度 int len = (*env)->GetArrayLength(env, jarr); //獲取傳遞進(jìn)來數(shù)組的元素,即數(shù)組首地址 jint* intArr = (*env)->GetIntArrayElements(env, jarr, 0); int i = 0; for (; i < len; i++) { //打印處理前的數(shù)組元素 LOGD("intArr[%d] = %d", i, intArr[i]); //遍歷數(shù)組元素+10 *(intArr + i) += 10; } return jarr; }
4,設(shè)備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)光設(shè)備Android.mk文件大年夜致就可以了,然則還須要解決一個(gè)版本兼容問題,做法是在jni目次下新建Application.mk文件,加上
APP_PLATFORM := android-8
5,編譯C說話代碼
[img]http://img.blog.csdn.net/20141210165245870
6,Java代碼中處理返回的數(shù)據(jù)
Java中傳遞數(shù)據(jù)到C代鋁闥楝C代碼處理完后返回給Java,這時(shí)刻Java拿到數(shù)據(jù)后就可以做本身的一些營業(yè)操作了,起首我們編譯完Native代碼后,先Refresh一下工程,然后clean一下工程,編寫如下的測試案例:
public class MainActivity extends Activity implements OnClickListener { // 加載本地庫文件 static { System.loadLibrary("Hello"); } private Button btn1, btn2, btn3; private DataProvider provider; @Override protected 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(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn1 : // 傳遞2個(gè)int給C代碼 int result = provider.add(3, 5); Toast.makeText(this, "相加的結(jié)不雅:" + 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數(shù)組給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; } } }運(yùn)行一下工程,留意:這里只能開啟arm模仿器,如不雅是x86模仿器會(huì)安裝apk時(shí)刻報(bào)錯(cuò),因?yàn)檫@段native代碼只編寫了arm支撐的版本,沒有支撐x86。如不雅在運(yùn)行測試的時(shí)刻出現(xiàn)一些缺點(diǎn)的話,請參考上篇文┞仿中提示慢慢解決。Android NDK開辟——常見缺點(diǎn)集錦以及LOG應(yīng)用
測試1:Java傳遞2個(gè)int給C
[img]http://img.blog.csdn.net/20141210170045337
Logcat輸出:
[img]http://img.blog.csdn.net/20141210170146453
測試2:Java傳遞string給C
[img]http://img.blog.csdn.net/20141210170237673
Logcat輸出:
[img]http://img.blog.csdn.net/20141210170317828
測試3:Java傳遞int數(shù)組給C
Logcat輸出:
[img]http://img.blog.csdn.net/20141210170428745
總結(jié):
上述這個(gè)簡單的示例可以解釋ndk開辟中,Java是如何將數(shù)據(jù)傳遞給C代碼的了,法度榜樣中只是簡單的介紹了3種數(shù)據(jù)類型int,string和int[],這是遠(yuǎn)遠(yuǎn)不敷的,因?yàn)镴ava支撐的數(shù)據(jù)類型比較多,這時(shí)刻怎么辦?好,有了膳綾擎的例子,我們可以觸類旁通了,在之前的博客中我也強(qiáng)調(diào)過ndk解壓包下的jni.h這個(gè)文件的重要性,這個(gè)文件不僅決定義了Java數(shù)據(jù)類型在C說話中的表示,看一下源碼,就發(fā)明一種一一映射的關(guān)系:
...... 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 */ #else typedef 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 */ ......別的還有一個(gè)異常重要的構(gòu)造體JNINativeInterface,這瑯綾擎定義了很多C函數(shù),只要看懂大年夜請安思就可以試著去調(diào)用這些函數(shù),這些函數(shù)在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*); ......源碼比較長,有興趣的同伙本身翻看一下,這里只貼部分。
源碼請?jiān)谶@里下載
相關(guān)案例查看更多
相關(guān)閱讀
- web服務(wù)
- 手機(jī)網(wǎng)站建設(shè)
- 云南微信小程序開發(fā)
- 網(wǎng)站建設(shè)選
- 出入小程序
- 網(wǎng)站建設(shè)百度官方
- 河南小程序制作
- 云南網(wǎng)站建設(shè)制作
- 云南網(wǎng)站建設(shè)電話
- 百度推廣
- 百度小程序
- 文山小程序開發(fā)
- 重慶網(wǎng)站建設(shè)公司
- 小程序的開發(fā)公司
- 云南小程序開發(fā)
- 生成海報(bào)
- 云南網(wǎng)站建設(shè)公司地址
- 網(wǎng)絡(luò)公司哪家好
- 云南花農(nóng)小程序
- 云南網(wǎng)站建設(shè)服務(wù)
- 云南省城鄉(xiāng)建設(shè)廳網(wǎng)站
- 云南網(wǎng)站制作哪家好
- 云南企業(yè)網(wǎng)站
- 云南小程序開發(fā)哪家好
- 云南小程序定制
- 國內(nèi)知名網(wǎng)站建設(shè)公司排名
- 網(wǎng)站建設(shè)首選公司
- vue開發(fā)小程序
- 微信分銷
- 報(bào)廢車