04.Eclipse下Ndk開發(以檔案拆分合併為例模擬一下開發過程,參考檔案加密的過程)

黑夜路口發表於2018-03-20

(建立於2017/12/6)

1.工具類PatchUtils

package com.ren.ndk_file_patch;

public class PatchUtils {
    
    static{
        System.loadLibrary("ndk_file_patch");
    }

    public native static void diff(String path,String path_pattern,int count);
    
    public native static void patch(String path_pattern,String merge_path,int count);
}

2.生成的標頭檔案 com_ren_ndk_file_patch_PatchUtils.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ren_ndk_file_patch_PatchUtils */

#ifndef _Included_com_ren_ndk_file_patch_PatchUtils
#define _Included_com_ren_ndk_file_patch_PatchUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ren_ndk_file_patch_PatchUtils
 * Method:    diff
 * Signature: (Ljava/lang/String;Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_ren_ndk_1file_1patch_PatchUtils_diff
  (JNIEnv *, jclass, jstring, jstring, jint);

/*
 * Class:     com_ren_ndk_file_patch_PatchUtils
 * Method:    patch
 * Signature: (Ljava/lang/String;Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_ren_ndk_1file_1patch_PatchUtils_patch
  (JNIEnv *, jclass, jstring, jstring, jint);

#ifdef __cplusplus
}
#endif
#endif

3.編寫的c檔案 ndk_file_patch.c

#include "com_ren_ndk_file_patch_PatchUtils.h"
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include<android/log.h>

#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"renzhenming",FORMAT,__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR),"renzhenming",FORMAT,__VA_ARGS__);

long get_file_size(const char *path){
    FILE *fl = fopen(path,"rb");
    //把與fp有關的檔案位置指標放到一個指定位置。
    //檔案指標定位到檔案末尾,偏移0個位元組 
    fseek(fl,0,SEEK_END);
    //函式用來獲取檔案讀寫指標的當前位置,對於二進位制檔案,則返回從檔案開頭到結尾的位元組數。
    return ftell(fl);
}

JNIEXPORT void JNICALL Java_com_ren_ndk_1file_1patch_PatchUtils_diff
  (JNIEnv *env, jclass jclz, jstring path_jstr, jstring path_pattern_jstr, jint file_num){

    //轉換檔案路徑
    const char *path = (*env)->GetStringUTFChars(env,path_jstr,NULL);
    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);
        //元素賦值
        //需要分割的檔案:C://jason/liuyan.png
        //子檔案:C://jason/liuyan_%d.png(path_pattern的格式)
        sprintf(patches[i],path_pattern,(i+1));
        LOGI("patch path:%s",patches[i]);
    }
    //不斷讀取path檔案,迴圈寫入file_num個檔案中
        //  整除
        //  檔案大小:90,分成9個檔案,每個檔案10
        //  不整除
        //  檔案大小:110,分成9個檔案,
        //  前(9-1)個檔案為(110/(9-1))=13
        //  最後一個檔案(110%(9-1))=6

    //獲取檔案大小
    int file_size = get_file_size(path);
    //開啟這個檔案
    FILE *fpr = fopen(path,"rb");

    //整除
    if(file_size % file_num == 0){
        //單個檔案大小
        int part = file_size/file_num;

        int i =0;
        //逐一寫入設定好的子檔案路徑中
        for(;i<file_num;i++){
            //從子檔案路徑開啟一個FILE
            FILE *fpw = fopen(patches[i],"wb");
            int j = 0;
            for(;j<part;j++){
                //邊讀邊寫
                //fgetc函式功能:從流中讀取字元,即從fp所指定的檔案中取得下一個字元。這裡需要注意,在每取完一個字元時fp會自動向下移動一個位元組。這樣編成時,程式設計師就不用再對fp                 //控制了。這種功能在許多讀寫函式中都有體現。
                fputc(fgetc(fpr),fpw);
            }
            fclose(fpw);
        }
    }else{
        //無法整除
        int part = file_size/(file_num-1);
        int i = 0;
        for(;i<file_num-1;i++){
            FILE *fpw = fopen(patches[i],"wb");
            int j=0;
            for(;j<part;j++){
                fputc(fgetc(fpr),fpw);
            }
            fclose(fpw);
        }

        //最後一個子檔案
        FILE *fpw = fopen(patches[file_num-1],"wb");
        i = 0;
        for(;i<file_size%(file_num-1);i++){
            fputc(fgetc(fpr),fpw);
        }
        fclose(fpw);
    }

    //釋放
    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);
}

/*
 * Class:     com_ren_ndk_file_patch_PatchUtils
 * Method:    patch
 * Signature: (Ljava/lang/String;Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_ren_ndk_1file_1patch_PatchUtils_patch
  (JNIEnv *env, jclass jclz, jstring path_pattern_jstr, jstring merge_path_jstr, jint file_num){
    //字串轉換
    const char *merge_path = (*env)->GetStringUTFChars(env,merge_path_jstr,NULL);
    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);
        //元素賦值
        //需要分割的檔案:C://jason/liuyan.png
        //子檔案:C://jason/liuyan_%d.png
        sprintf(patches[i],path_pattern,i+1);
        LOGI("patch path:%s",patches[i]);
    }
    //開啟要merge_path為一個FILE
    FILE *fpw = fopen(merge_path,"wb");
    //把所有分割的檔案讀取一遍,寫入到這個總的檔案中
    i = 0;
    for(;i<file_num;i++){
        //得到每個子檔案的大小
        int file_size = get_file_size(patches[i]);
        //開啟每個子檔案路徑為一個FILE
        FILE *fpr = fopen(patches[i],"rb");
        int j =0;
        for(;j<file_size;j++){
            fputc(fgetc(fpr),fpw);
        }
        fclose(fpr);
    }
    fclose(fpw);

    //釋放記憶體
    i = 0;
    for(;i<file_num;i++){
        free(patches[i]);
    }
    free(patches);

    (*env)->ReleaseStringUTFChars(env,merge_path_jstr,merge_path);
    (*env)->ReleaseStringUTFChars(env,path_pattern_jstr,path_pattern);
}

4.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   //使用了log,需要引入

include $(BUILD_SHARED_LIBRARY)

5.開始拆分合並

public class MainActivity extends Activity {
    private String SD_CARD_PATH;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SD_CARD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
    }
    
    public void diff(View view){
        String path = SD_CARD_PATH +File.separatorChar+ "2.mp4";
        String path_pattern = SD_CARD_PATH +File.separatorChar+ "2_%d.mp4";
        PatchUtils.diff(path, path_pattern, 2);
        System.out.println("拆分完成");
    }
    public void patch(View view){
        String merge_path = SD_CARD_PATH +File.separatorChar+ "2_new.mp4";
        String path_pattern = SD_CARD_PATH +File.separatorChar+ "2_%d.mp4";
        PatchUtils.patch(path_pattern, merge_path, 2);
        System.out.println("合併完成");
    }
}

6.注意新增許可權,6.0動態申請

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


相關文章