上一節我們學習的是JNI的調用,從最基本的配置到獨立去寫一個JNI的過程,這里我們進入NDK的學習,在這篇文章我們從文件的拆分和合并兩個方面來進行學習NDK的知識
NDK:Native Development 是一系列工具的集合,它提供了一系列的工具,幫助開發者快速開發C/C++的動態庫,并能自動將so和java一起打包成apk
JNI:Java Native Interface 是java語言提供的java和C/C++相互溝通的機制,Java可以通過JNI調用C/C++代碼,C/C++的代碼也可以調用java代碼
-
使用NDK開發有以下的有點
- 項目需要調用底層的一些C/C++的一些東西,或者已經在C/C++環境下實現了功能代碼,直接使用即可。NDK開發常用于驅動開發、熱點共享、數學運算、實時渲染游戲、音視頻處理、文件壓縮、人臉識別、圖片處理
- 為了效率更加的高效,將要求高性能的應用邏輯使用C/C++開發,從而提高應用程序的執行效率,但是C/C++代碼雖然是高效的,在java與C/C++相互調用時卻增加了開銷
- 基于安全性的考慮,防止代碼被反編譯,為了安全起見,使用C/C++語言來編寫重要的部分以增加系統的安全性,最后生成so庫,便于給人提供方便。
- 便于移植,用C/C++寫的庫可以方便在其他的嵌入式平臺上再次使用,例如在IOS也可以使用Android的so文件
正文
NDK配置
下載NDK的包并將Eclipse中的配置NDK的相關的路徑,接下來按照上篇中講解的JNI的那樣,先寫一個native方法并使用javah生成.h文件
然后添加native支持
在這里我們使用的是C語言,所以在Android.mk中LOCAL_SRC_FILES使用的是.c,jni目錄下也是.c文件
我們先來看一下Android.mk文件的內容
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) 設置當前的編譯目錄
- include $(CLEAR_VARS) 清除LOCAL_XX變量(LOCAL_PATH除外)
- LOCAL_MODULE指定當前編譯模塊的名稱
- LOCAL_SRC_FILES指代相應的.c文件
- LOCAL_LDLIBS:= -llog代表著能夠在c中打印AndroidLog
- include $(BUILD_SHARED_LIBRARY) 動態庫;BUILD_STATIC_LIBRARY:靜態庫, BUILD_EXECUTEABLE指:可執行文件
當將生成的.h文件放到jni目錄下會看到jni.h會找不到,這時候需要配置一下路徑
這樣我們就把NDK的環境配置就弄好了,我們build project就會在lib的下面生成相應的so文件
以上是EclipseNDK的配置,AndroidStudio的配置類似
在這里配置上NDK的路徑,然后在extend tool里面配置javah和ndk build
就像這樣,接下來的工作就和在Eclipse里面的操作一樣了
文件的拆分
首先我們在c中開發時打印AndroidLog需要引入相應的.h
#include <android/log.h>
具體的代碼
__android_log_print(ANDROID_LOG_WARN,"lypop","This file_num is:%d",file_num);
相應的拆分步驟:
-
獲取到相應的分割文件的路徑,也就是講jstring轉化為char*
const char* path = (*env)->GetStringUTFChars(env,path_jstr,JNI_FALSE); const char* path_pattern = (*env)->GetStringUTFChars(env,path_pattern_jstr,NULL);
-
得到分割之后子文件的路徑列表信息(這里使用了二級指針來存儲)
char **patches = malloc(sizeof(char*)*file_num); int i = 0; for(; i <file_num; i++){ patches[i] = malloc(sizeof(char) * 100); //元素進行賦值 sprintf(patches[i],path_pattern,(i+1)); __android_log_print(ANDROID_LOG_WARN,"lypop","patches[%d]:%s",i,patches[i]); }
-
得到文件的大小并計算每個部分的大小
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;
-
開始對每個文件進行寫入
for(; i < file_num; i++){ FILE *fpw = fopen(patches[i],"wb"); int j = 0; for(; j < part; j++){ //邊讀邊寫 fputc(fgetc(fpr),fpw); } if(i == (file_num - 1)){ j = 0; for(; j < (fileSize % file_num); j++){ //邊讀邊寫 fputc(fgetc(fpr),fpw); } } fclose(fpw); } fclose(fpr);
注意的是這里在最后的時候判斷是否寫到最后一個文件,當前如果是最后一個文件則最后將多出來的字節寫入到最后一個文件中
-
釋放相應的指針資源
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);
這樣就實現了對一個文件的拆分成若干個文件
文件的合并
文件的拆分和文件的合并過程是向逆的,核心代碼
for(; i < file_num; i++){
//每個子文件的大小
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的簡單使用,自己也是新手,希望對你有所幫助。