美文网首页Android开发经验谈Android开发Android技术知识
NDK开发(六) :JNI实现文件拆分和合并

NDK开发(六) :JNI实现文件拆分和合并

作者: 103style | 来源:发表于2019-08-20 19:24 被阅读3次

    转载请以链接形式标明出处:
    本文出自:103style的博客

    本文操作以 Android Studio 3.4.2 版本为例


    目录

    • 编写测试代码
    • 实现创建文件逻辑
    • 实现JNI文件拆分逻辑
    • 实现JNI文件合并逻辑
    • 执行测试代码

    编写测试代码

    • 添加权限
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      
    • 创建JniFileOperation类,编写对应的测试代码:
      public class JniFileOperation {
          private static final String TAG = "JniFileOperation";
          static {
              System.loadLibrary("file_operation");
          }
          /**
           * 原文件名
           */
          private String fileName = "split_test.txt";
          /**
           * 合并拆分之后文件的文件名
           */
          private String mergeFileName = "split_test_merged.txt";
          /**
           * 文件拆分格式
           */
          private String splitFileFormat = "split_test_%d.txt";
          /**
           * 拆分的数量
           */
          private int splitCount = 4;
          public native void createFile(String fileName);
          /**
           * 拆分
           *
           * @param path        文件路径
           * @param pathPattern 拆分之后文件的路径格式
           * @param splitCount  拆分成几个
           */
          public native void split(String path, String pathPattern, int splitCount);
          /**
           * 合并
           *
           * @param pathMerge   合并之后的文件路径
           * @param pathPattern 要合并的文件的路径格式
           * @param count       要合并的文件数量
           */
          public native void merge(String pathMerge, String pathPattern, int count);
      
          /**
           * 测试文件 拆分与合并
           */
          public void test() {
              String filePath = Config.getBaseUrl() + fileName;
              Log.e(TAG, "filePath = " + filePath);
      
              File file = new File(filePath);
              if (!file.exists()) {
                  Log.e(TAG, "开始创建文件");
                  createFile(filePath);
              }
      
              String pathPattern = Config.getBaseUrl() + splitFileFormat;
              split(filePath, pathPattern, splitCount);
              Log.e(TAG, "文件拆分成功");
      
              String mergePath = Config.getBaseUrl() + mergeFileName;
              merge(mergePath, pathPattern, splitCount);
              Log.e(TAG, "文件合并成功");
          }
      }
      
    • 创建file_operation.cpp
    • 添加以下代码到CMakeLists.txt
      add_library(
              file_operation
              SHARED
              file_operation.cpp)
      target_link_libraries(
              file_operation
              ${log-lib})
      

    实现创建文件逻辑

    #include <jni.h>
    #include <cstdio>
    #include <cstdlib>
    #include <android/log.h>
    
    #define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"com_lxk_ndkdemo",FORMAT,##__VA_ARGS__);
    
    const int SIZE = 100;
    
    /**
     * 创建拆分原文件
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_lxk_ndkdemo_JniFileOperation_createFile(JNIEnv *env, jobject instance, jstring fileName_) {
        const char *fileName = env->GetStringUTFChars(fileName_, nullptr);
    
        //创建写文件流
        FILE *fp = fopen(fileName, "wb");
    
        //写文件
        for (int i = 0; i < SIZE; i++) {
            fputs("0123456789\n", fp);
        }
        //关闭流
        fclose(fp);
        LOGE("%s", "创建文件成功");
        env->ReleaseStringUTFChars(fileName_, fileName);
    }
    

    实现JNI文件拆分逻辑

    /**
     * 根据文件的路径,获得文件的大小
     */
    long get_file_size(const char *path) {
        //rb:只读打开一个二进制文件,允许读数据
        //使用给定的模式 "rb" 打开 path 所指向的文件
        FILE *fp = fopen(path, "rb");
        if (fp == nullptr) {
            LOGE("%s", "文件打开失败");
            return 0;
        }
        //SEEK_SET  文件的开头
        //SEEK_CUR  文件指针的当前位置
        //SEEK_END  文件的末尾
        //设置流 fp 的文件位置为 0, 0 意味着从给定的 SEEK_END 位置查找的字节数。
        fseek(fp, 0, SEEK_END);
        //返回给定流 fp 的当前文件位置。
        return ftell(fp);
    }
    /**
     * 拆分文件
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_lxk_ndkdemo_JniFileOperation_split(JNIEnv *env, jobject instance, jstring path_,
                                                jstring pathPattern_, jint splitCount) {
        const char *path = env->GetStringUTFChars(path_, nullptr);
        const char *pathPattern = env->GetStringUTFChars(pathPattern_, nullptr);
    
        //malloc:分配所需的内存空间,并返回一个指向它的指针。
        char **patches = new char *[splitCount];
    
        //获取文件长度
        long fileSize = get_file_size(path);
    
        //获取单个文件长度
        long per_size = fileSize / splitCount;
    
        //设置每个子文件的路径
        for (int i = 0; i < splitCount; i++) {
            patches[i] = new char[256];
            sprintf(patches[i], pathPattern, i);
            LOGE("patches[%d] = %s", i, patches[i]);
        }
    
        //创建fp流读取path对应的文件
        FILE *fp = fopen(path, "rb");
        if (fp == nullptr) {
            LOGE("%s", "文件打开失败");
            return;
        }
        //读取分割文件的流
        FILE *index_fp = nullptr;
        int index = 0;
        for (int i = 0; i < fileSize; i++) {
            if (i % per_size == 0) {
                if (index_fp != nullptr) {
                    fclose(index_fp);
                }
                index_fp = fopen(patches[index], "wb");
                index++;
                if (index_fp == nullptr) {
                    LOGE("文件%s打开失败", patches[index]);
                    return;
                }
            }
            fputc(fgetc(fp), index_fp);
    
            //读完之后释放流
            if (i + 1 == fileSize) {
                fclose(index_fp);
            }
        }
        fclose(fp);
    
        //释放内存
        for (int i = 0; i < splitCount; i++) {
            free(patches[i]);
        }
        free(patches);
    
        env->ReleaseStringUTFChars(path_, path);
        env->ReleaseStringUTFChars(pathPattern_, pathPattern);
    }
    

    实现JNI文件合并逻辑

    /**
     * 合并拆分文件
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_lxk_ndkdemo_JniFileOperation_merge(JNIEnv *env, jobject instance, jstring pathMerge_,
                                                jstring pathPattern_, jint count) {
        const char *pathMerge = env->GetStringUTFChars(pathMerge_, nullptr);
        const char *pathPattern = env->GetStringUTFChars(pathPattern_, nullptr);
    
        //创建合并文件的写流
        FILE *fp = fopen(pathMerge, "wb");
    
        for (int i = 0; i < count; i++) {
            char *index = new char[256];
            sprintf(index, pathPattern, i);
            //读取每个分割文件
            FILE *index_fp = fopen(index, "rb");
            if (index_fp == nullptr) {
                LOGE("文件%s读取失败", index)
                return;
            }
            //依次写入合并文件
            int ch;
            while ((ch = fgetc(index_fp)) != EOF) {
                fputc(ch, fp);
            }
            //关闭当前的分割文件流
            fclose(index_fp);
            //释放拆分文件名的内存
            free(index);
        }
        fclose(fp);
    
        env->ReleaseStringUTFChars(pathMerge_, pathMerge);
        env->ReleaseStringUTFChars(pathPattern_, pathPattern);
    }
    

    执行测试代码

    private void testFileOperation() {
        if (hasFilePermission()) {
            new JniFileOperation().test();
            Toast.makeText(this, "任务完成,测试文件路径:" + Config.getBaseUrl(), Toast.LENGTH_SHORT).show();
        }
    }
    

    会在/storage/emulated/0/NDKDemo/下生成对应的测试文件。


    Demo地址:https://github.com/103style/NDKDoc/tree/master/NDKDemo

    以上

    相关文章

      网友评论

        本文标题:NDK开发(六) :JNI实现文件拆分和合并

        本文链接:https://www.haomeiwen.com/subject/facpsctx.html