上一節(jié)我們學(xué)習(xí)的是JNI的調(diào)用,從最基本的配置到獨(dú)立去寫(xiě)一個(gè)JNI的過(guò)程,這里我們進(jìn)入NDK的學(xué)習(xí),在這篇文章我們從文件的拆分和合并兩個(gè)方面來(lái)進(jìn)行學(xué)習(xí)NDK的知識(shí)
NDK:Native Development 是一系列工具的集合,它提供了一系列的工具,幫助開(kāi)發(fā)者快速開(kāi)發(fā)C/C++的動(dòng)態(tài)庫(kù),并能自動(dòng)將so和java一起打包成apk
JNI:Java Native Interface 是java語(yǔ)言提供的java和C/C++相互溝通的機(jī)制,Java可以通過(guò)JNI調(diào)用C/C++代碼,C/C++的代碼也可以調(diào)用java代碼
-
使用NDK開(kāi)發(fā)有以下的有點(diǎn)
- 項(xiàng)目需要調(diào)用底層的一些C/C++的一些東西,或者已經(jīng)在C/C++環(huán)境下實(shí)現(xiàn)了功能代碼,直接使用即可。NDK開(kāi)發(fā)常用于驅(qū)動(dòng)開(kāi)發(fā)、熱點(diǎn)共享、數(shù)學(xué)運(yùn)算、實(shí)時(shí)渲染游戲、音視頻處理、文件壓縮、人臉識(shí)別、圖片處理
- 為了效率更加的高效,將要求高性能的應(yīng)用邏輯使用C/C++開(kāi)發(fā),從而提高應(yīng)用程序的執(zhí)行效率,但是C/C++代碼雖然是高效的,在java與C/C++相互調(diào)用時(shí)卻增加了開(kāi)銷(xiāo)
- 基于安全性的考慮,防止代碼被反編譯,為了安全起見(jiàn),使用C/C++語(yǔ)言來(lái)編寫(xiě)重要的部分以增加系統(tǒng)的安全性,最后生成so庫(kù),便于給人提供方便。
- 便于移植,用C/C++寫(xiě)的庫(kù)可以方便在其他的嵌入式平臺(tái)上再次使用,例如在IOS也可以使用Android的so文件
正文
NDK配置
下載NDK的包并將Eclipse中的配置NDK的相關(guān)的路徑,接下來(lái)按照上篇中講解的JNI的那樣,先寫(xiě)一個(gè)native方法并使用javah生成.h文件
然后添加native支持

在這里我們使用的是C語(yǔ)言,所以在Android.mk中LOCAL_SRC_FILES使用的是.c,jni目錄下也是.c文件
我們先來(lái)看一下Android.mk文件的內(nèi)容
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ndk_file_patch
LOCAL_SRC_FILES := ndk_file_patch.c
LOCAL_LDLIBS:= -llog
include $(BUILD_SHARED_LIBRARY)
tips:
- LOCAL_PATH := $(call my-dir) 設(shè)置當(dāng)前的編譯目錄
- include $(CLEAR_VARS) 清除LOCAL_XX變量(LOCAL_PATH除外)
- LOCAL_MODULE指定當(dāng)前編譯模塊的名稱(chēng)
- LOCAL_SRC_FILES指代相應(yīng)的.c文件
- LOCAL_LDLIBS:= -llog代表著能夠在c中打印AndroidLog
- include $(BUILD_SHARED_LIBRARY) 動(dòng)態(tài)庫(kù);BUILD_STATIC_LIBRARY:靜態(tài)庫(kù), BUILD_EXECUTEABLE指:可執(zhí)行文件
當(dāng)將生成的.h文件放到j(luò)ni目錄下會(huì)看到j(luò)ni.h會(huì)找不到,這時(shí)候需要配置一下路徑

這樣我們就把NDK的環(huán)境配置就弄好了,我們build project就會(huì)在lib的下面生成相應(yīng)的so文件

以上是EclipseNDK的配置,AndroidStudio的配置類(lèi)似

在這里配置上NDK的路徑,然后在extend tool里面配置javah和ndk build

就像這樣,接下來(lái)的工作就和在Eclipse里面的操作一樣了
文件的拆分
首先我們?cè)赾中開(kāi)發(fā)時(shí)打印AndroidLog需要引入相應(yīng)的.h
#include <android/log.h>
具體的代碼
__android_log_print(ANDROID_LOG_WARN,"lypop","This file_num is:%d",file_num);
相應(yīng)的拆分步驟:
-
獲取到相應(yīng)的分割文件的路徑,也就是講jstring轉(zhuǎn)化為char*
const char* path = (*env)->GetStringUTFChars(env,path_jstr,JNI_FALSE); const char* path_pattern = (*env)->GetStringUTFChars(env,path_pattern_jstr,NULL); -
得到分割之后子文件的路徑列表信息(這里使用了二級(jí)指針來(lái)存儲(chǔ))
char **patches = malloc(sizeof(char*)*file_num); int i = 0; for(; i <file_num; i++){ patches[i] = malloc(sizeof(char) * 100); //元素進(jìn)行賦值 sprintf(patches[i],path_pattern,(i+1)); __android_log_print(ANDROID_LOG_WARN,"lypop","patches[%d]:%s",i,patches[i]); } -
得到文件的大小并計(jì)算每個(gè)部分的大小
long getFileSize(char *path){ FILE *fp = fopen(path,"rb"); fseek(fp,0,SEEK_END); return ftell(fp); } int fileSize = getFileSize(path); FILE *fpr = fopen(path,"rb"); int part = fileSize / file_num; -
開(kāi)始對(duì)每個(gè)文件進(jìn)行寫(xiě)入
for(; i < file_num; i++){ FILE *fpw = fopen(patches[i],"wb"); int j = 0; for(; j < part; j++){ //邊讀邊寫(xiě) fputc(fgetc(fpr),fpw); } if(i == (file_num - 1)){ j = 0; for(; j < (fileSize % file_num); j++){ //邊讀邊寫(xiě) fputc(fgetc(fpr),fpw); } } fclose(fpw); } fclose(fpr);
注意的是這里在最后的時(shí)候判斷是否寫(xiě)到最后一個(gè)文件,當(dāng)前如果是最后一個(gè)文件則最后將多出來(lái)的字節(jié)寫(xiě)入到最后一個(gè)文件中
-
釋放相應(yīng)的指針資源
i = 0; for(; i < file_num; i++){ free(patches[i]); } free(patches); (*env)->ReleaseStringUTFChars(env,path_jstr,path); (*env)->ReleaseStringUTFChars(env,path_pattern_jstr,path_pattern);
這樣就實(shí)現(xiàn)了對(duì)一個(gè)文件的拆分成若干個(gè)文件
文件的合并
文件的拆分和文件的合并過(guò)程是向逆的,核心代碼
for(; i < file_num; i++){
//每個(gè)子文件的大小
int fileSize = getFileSize(patches[i]);
FILE *fpr = fopen(patches[i],"rb");
int j = 0;
for(; j < fileSize; j++){
fputc(fgetc(fpr),fpw);
}
fclose(fpr);
}
fclose(fpw);
以上就是NDK的簡(jiǎn)單使用,自己也是新手,希望對(duì)你有所幫助。