上一节我们学习的是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的简单使用,自己也是新手,希望对你有所帮助。