概述
之前描述了一些JNI的基础还有静态注册和动态注册,现在则通过一个简单的demo来总结一些JNI开发时的基本流程
文件拆分和合并
- 首先新建一个包含ndk项目,当然也可以不包含ndk,后续通过手动添加配置即可
如果创建的文件不包含ndk,那么需要手动添加(添加方式)
并且在CMakeList.txt文件中进行配置
#这是cmake版本信息
cmake_minimum_required(VERSION 3.4.1)
# 这是编译后的so库和cpp文件路径
add_library( # Sets the name of the library.
#编译后的so库文件名
native-lib
#动态库类型,一般用SHARED
# Sets the library as a shared library.
SHARED
#cpp文件路径
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
#导入的系统库,这里导入log库用于日志输出
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#链接系统的log库到我们的native-lib库
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
这样一个简单的CMakeList文件就编写好了,当然CMakeList语法不仅仅这些,后续有机会再继续
- 定义拆分和合并的方法
//拆分
external fun split(inputPath: String, outputPath: String, number: Int)
//合并
external fun merge(inputPath: String, outputPath: String, number: Int)
3.1. 实现native方法
首先导入基础库,并且定义日志打印方法
#include <jni.h>
#include <stdio.h>
#include <errno.h>
#include <string>
#include <android/log.h>
#define TAG "tianyl"
#define LOGI(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
3.2. 然后定义工具的方法
//获取文件大小
long getFileSize(const char *path) {
LOGI("fopen path %s", path);
FILE *fp = fopen(path, "rb");
if (fp == NULL) {
LOGI("fopen error %d\n", errno);
return 0;
}
fseek(fp, 0, SEEK_END);
long ret = ftell(fp);
fclose(fp);
return ret;
}
3.3. 实现拆分方法
extern "C" JNIEXPORT void JNICALL
Java_com_demo_tianyl_jnidemo_MainActivity_split(
JNIEnv *env, jobject /* this */, jstring inputPath, jstring outputPath, jint fileNum) {
LOGI("java的String转换成c的string");
//java的String转换成c的string
const char *inputPathStr = env->GetStringUTFChars(inputPath, NULL);
const char *outputPathStr = env->GetStringUTFChars(outputPath, NULL);
LOGI("申请字符数组, 存放子文件名");
//申请字符数组, 存放子文件名
char **paths = (char **) malloc(sizeof(char *) * fileNum);
LOGI("每个文件名申请地址 char = %d char * = %d", sizeof(char), sizeof(char *));
int i = 0;
for (; i < fileNum; i++) {
//每个文件名申请地址
//每个文件名申请地址
paths[i] = (char *) malloc(sizeof(char) * 100);
// 需要分割的文件 xxx
// 每个子文件名称 xxx_n
sprintf(paths[i], outputPathStr, i);// 格式化文件名,给拆分后的加后缀
}
LOGI("准备拆分");
int fileSize = getFileSize(inputPathStr);
if (fileSize == 0) {
free(paths);
env->ReleaseStringUTFChars(inputPath, inputPathStr);
env->ReleaseStringUTFChars(outputPath, outputPathStr);
LOGI("文件拆分失败 1");
return;
}
FILE *fpr = fopen(inputPathStr, "rb");
LOGI("开始拆分");
/*
* 1.判断文件大小能够被 fileNum整除
* 2.能整除就平分
* 3.不能整除就先分 fileNum -1 ,剩下的给最后一个
* */
if (fileSize % fileNum == 0) {
int part = fileSize / fileNum;
for (int i = 0; i < fileNum; i++) {
FILE *fpw = fopen(paths[i], "wb");//文件已经存在 就删除,只运行写
for (int j = 0; j < part; j++) {
fputc(fgetc(fpr), fpw);
}
fclose(fpw);
}
} else {
int part = fileSize / (fileNum - 1);
for (int i = 0; i < fileNum - 1; i++) {
FILE *fpw = fopen(paths[i], "wb");//文件已经存在 就删除,只运行写
for (int j = 0; j < part; j++) {
fputc(fgetc(fpr), fpw);
}
fclose(fpw);
}
FILE *fpw = fopen(paths[fileNum - 1], "wb");
for (int i = 0; i < fileSize % (fileNum - 1); i++) {
fputc(fgetc(fpr), fpw);
}
fclose(fpw);
}
LOGI("拆分结束");
fclose(fpr);
for (int i = 0; i < fileNum; i++) {
free(paths[i]);
}
free(paths);
env->ReleaseStringUTFChars(inputPath, inputPathStr);
env->ReleaseStringUTFChars(outputPath, outputPathStr);
LOGI("拆分return");
return;
}
3.4. 实现合并方法
extern "C" JNIEXPORT void JNICALL
Java_com_demo_tianyl_jnidemo_MainActivity_merge
(JNIEnv *env, jobject /* this */, jstring inputPath, jstring outputPath, jint fileNum)
{
LOGI("java的String转换成c的string");
//java的String转换成c的string
const char *inputPathStr = env->GetStringUTFChars(inputPath, NULL);
const char *outputPathStr = env->GetStringUTFChars(outputPath, NULL);
LOGI("申请字符数组, 存放子文件名");
//申请字符数组, 存放子文件名
char **paths = (char **) malloc(sizeof(char *) * fileNum);
int i = 0;
for (; i < fileNum; i++) {
//每个文件名申请地址
//每个文件名申请地址
paths[i] = (char *) malloc(sizeof(char) * 100);
// 需要合并的文件 xxx
// 每个子文件名称 xxx_n
sprintf(paths[i], outputPathStr, i);// 格式化文件名
}
FILE *fpw = fopen(outputPathStr, "wb");
for (int i =0; i < fileNum; i++) {
int fileSize = getFileSize(paths[i]);
FILE *fpr = fopen(paths[i], "rb");
for (int j =0; j < fileSize; j++) {
fputc(fgetc(fpr), fpw);
}
fclose(fpr);
}
fclose(fpw);
for (int i =0; i< fileNum; i++) {
free(paths[i]); //每一个malloc 对应一个free
}
free(paths);
env->ReleaseStringUTFChars(inputPath, inputPathStr);
env->ReleaseStringUTFChars(outputPath, outputPathStr);
LOGI("合并结束return");
}
以上,一个简单的JniDemo就完成了,当然,最后别忘记了添加权限,并且在系统中允许,大坑,切记!
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>