NDK開發(六) :JNI實現文件拆分和合並

轉載請以連接形式標明出處: 本文出自:103style的博客android

本文操做以 Android Studio 3.4.2 版本爲例git


NDK開發文章彙總github


目錄

  • 編寫測試代碼
  • 實現建立文件邏輯
  • 實現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/下生成對應的測試文件。bash


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

若是以爲不錯的話,請幫忙點個讚唄。ui

以上this


掃描下面的二維碼,關注個人公衆號 Android1024, 點關注,不迷路。 spa

Android1024
相關文章
相關標籤/搜索