開始之前
本篇博文承接上篇Android NDK開發(fā)(一) 入門, 如果大家沒有接觸過NDK開發(fā)請(qǐng)移至上篇
本篇主要通過HEX的編碼和解碼案例來進(jìn)一步了解NDK的開發(fā).
什么是HEX ?
就是16進(jìn)制!
什么是HEX的編碼和解碼 ?
編碼: 就是將字節(jié)數(shù)組每個(gè)字節(jié)使用16進(jìn)制的可見字符串來顯示 bytes --> String
解碼: 就是將16進(jìn)制的可見字符串轉(zhuǎn)換為原來的字節(jié)數(shù)組 String --> bytes
來張圖說明一下:
開始開發(fā)
今天我們不采用上一篇中創(chuàng)建工程時(shí)直接勾選出 Include C++ Support, 而是創(chuàng)建一個(gè)普通的工程, 后期如何擴(kuò)展為NDK的工程
創(chuàng)建工程
創(chuàng)建普通工程, 修改local.properties文件, 添加ndk.dir=XXX;
-
手動(dòng)在app目錄下創(chuàng)建CMakeLists.txt文件, 內(nèi)容和注釋如下:
# CMake的編譯腳本配置文件 # 1. 標(biāo)注需要支持的CMake最小版本 cmake_minimum_required(VERSION 3.4.1) # 2. add_library 定義需要編譯的代碼庫(kù) 名稱, 類型, 包含的源碼 add_library( # Sets the name of the library. codec # Sets the library as a shared library. SHARED src/main/cpp/com_lulu_encodedemo_Codec.c ) # 3. find_library 定義當(dāng)前代碼庫(kù)需要依賴的系統(tǒng)或者第三方庫(kù)文件 find_library( log_lib # 指定要查找的系統(tǒng)庫(kù), 給一個(gè)名字 log # 真正要查找的liblog.so或者liblog.a ) # 可以寫多個(gè) find_library # 4. target_link_libraries設(shè)置最終編譯的目標(biāo)代碼庫(kù) target_link_libraries( codec # add_library 生成的 ${log_lib} # find_library 找到的系統(tǒng)庫(kù) )
-
修改app目錄下的build.gradle文件, 在android便簽下添加
externalNativeBuild { cmake { path "CMakeLists.txt" } }
在src/main目錄下創(chuàng)建cpp文件夾. 至此工程創(chuàng)建完畢
編寫業(yè)務(wù)邏輯
創(chuàng)建Codec.java類, 用于綁定C代碼
public class Codec {
static {
//一定不要忘記調(diào)用!!!!
System.loadLibrary("codec");
}
public static native String hexEncode(byte[] data);
public static native byte[] hexDecode(String data);
}
Note : 給大家忠告, System.loadLibrary("codec");一定不要忘記調(diào)用,否則會(huì)報(bào)一下錯(cuò)誤
Paste_Image.png
實(shí)現(xiàn)C語言核心部分
對(duì)應(yīng)Codec類有兩個(gè)方法分別用于編碼和解碼, 代碼中已經(jīng)做了詳細(xì)的注釋加以說明
-
編碼方法:
// 代表可以被 Java 調(diào)用 JNIEXPORT // 返回值類型 jstring //聲明遵守JNI Java 調(diào)用C的規(guī)則 JNICALL Java_com_lulu_encodedemo_Codec_hexEncode(JNIEnv *env, jclass clazz, jbyteArray array) { // 1. 數(shù)組長(zhǎng)度斑司;2. new StringBuilder(); or char[len * 2] 3. char[] -> jstring jstring ret = NULL; if (array != NULL) { //得到數(shù)組的長(zhǎng)度 jsize len = (*env)->GetArrayLength(env, array); if (len > 0) { //存儲(chǔ)編碼后的字符, +1的原因是考慮到\0 char chs[len * 2 + 1]; jboolean b = JNI_FALSE; //得到數(shù)據(jù)的原始數(shù)據(jù) 此處注意要取b的地址! jbyte *data = (*env)->GetByteArrayElements(env, array, &b); int index; for (index = 0; index < len; index++) { jbyte bc = data[index]; //拆分成高位, 低位 jbyte h = (jbyte) ((bc >> 4) & 0x0f); jbyte l = (jbyte) (bc & 0x0f); //把高位和地位轉(zhuǎn)換成字符 jchar ch; jchar cl; if (h > 9) { ch = (jchar) ('A' + (h - 10)); } else { ch = (jchar) ('0' + h); } if (l > 9) { cl = (jchar) ('A' + (l - 10)); } else { cl = (jchar) ('0' + l); } //轉(zhuǎn)換之后拼接 chs[index * 2] = (char) ch; chs[index * 2 + 1] = (char) cl; } //最后一位置為0 chs[len * 2] = 0; //釋放數(shù)組 (*env)->ReleaseByteArrayElements(env, array, data, JNI_ABORT); ret = (*env)->NewStringUTF(env, chs); } } return ret; }
-
解碼方法:
JNIEXPORT jbyteArray JNICALL Java_com_lulu_encodedemo_Codec_hexDecode(JNIEnv *env, jclass type, jstring str) { jbyteArray ret = NULL; if (str != NULL) { // TODO jsize len = (*env)->GetStringLength(env, str); //判斷只有在長(zhǎng)度為偶數(shù)的情況下才繼續(xù) if (len % 2 == 0) { jsize dLen = len >> 1; jbyte data[dLen]; jboolean b = JNI_FALSE; const jchar *chs = (*env)->GetStringChars(env, str, &b); int index; for (index = 0; index < dLen; index++) { //獲取到單個(gè)字符 jchar ch = chs[index * 2]; jchar cl = chs[index * 2 + 1]; jint h = 0; jint l = 0; //得到高位和低位的 ascii if (ch >= 'A') { h = ch - 'A' + 10; } else if (ch >= 'a') { h = ch - 'a' + 10; } else if(ch >= '0') { h = ch - '0'; } if (cl >= 'A') { l = cl - 'A' + 10; } else if (cl >= 'a') { l = cl - 'a' + 10; } else if(cl >= '0'){ l = cl - '0'; } //高位和地位拼接 data[index] = (jbyte) ((h << 4) | l); } //釋放 (*env)->ReleaseStringChars(env, str, chs); //創(chuàng)建新的字節(jié)數(shù)組 ret = (*env)->NewByteArray(env, dLen); //給新創(chuàng)建的數(shù)組設(shè)置數(shù)值 (*env)->SetByteArrayRegion(env, ret, 0,dLen, data); } } return ret; }
代碼測(cè)試:
String s = Codec.hexEncode(new byte[]{0x3c, 0x7c});
Log.d(TAG, "onCreate: s ==> " + s);
byte[] bytes = Codec.hexDecode(s);
for (byte aByte : bytes) {
Log.d(TAG, "onCreate: byte ==> " + String.format(Locale.CANADA, "%x",aByte ));
}
測(cè)試結(jié)果:
D/MainActivity: onCreate: s ==> 3C7C
D/MainActivity: onCreate: byte ==> 3c
D/MainActivity: onCreate: byte ==> 7c
完整代碼
代碼已上傳至Github上, 歡迎大家Clone
總結(jié)
至此NDK的博客完成了, 本人也是菜鳥一枚, 希望更多的是拋磚引玉, 讓各位大神們指出修改意見!
寫博客不易, 期待大家的鼓勵(lì)和支持, 謝謝v