Android NDK开发之旅16--NDK--文件拆分与合并

Android NDK开发之旅 目录

1.JNI声明文件

package com.haocai.ndktest;

/**
 * Created by Xionghu on 2017/11/9.
 * Desc:
 */

public class NDKFileUtils {

    /**
     * 拆分
     * @param path 完整包路径
     * @param path_pattern 分包路径
     * @param count
     */
    public native static void diff(String path,String path_pattern,int count);


    /**
     * 合并
     * @param path_pattern 分包路径
     * @param count
     * @param merge_path 完整包路径
     */
    public native static void patch(String path_pattern,int count,String merge_path);

    static {
        System.loadLibrary("mylibrary");
    }

}

2.javah生成的头文件

怎样生成头文件请参考超级简单的Android Studio jni 实现(无需命令行)

注意:新版Android Studio ndk-build 要指定Application.mk和Android.mk路径



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

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

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

#ifdef __cplusplus
}
#endif
#endif

3.根据生成的头文件写C程序

#include "com_haocai_ndktest_NDKFileUtils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <android/log.h>

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

//获取文件大小
long get_file_size( char *path){
     FILE *fp = fopen(path,"rb");
     fseek(fp,0,SEEK_END);
     return ftell(fp);

}

JNIEXPORT void JNICALL Java_com_haocai_ndktest_NDKFileUtils_diff
(JNIEnv * env, jclass jcls, jstring path_jstr, jstring path_pattern_jstr, jint file_num) {
    //jstring -> char*
    //需要分割的文件路径
    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 = 0; i < file_num; i++) {
        patches[i] = malloc(sizeof(char) * 100);
        //元素复制
        //需要分割的文件:C://jason/
        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
    long filesize = get_file_size((char*)path);
    FILE *fpr = fopen(path, "rb");
    //整除
    if (filesize % file_num == 0) {
        //单个文件大小
        int part = filesize / file_num;
        i = 0;
        //逐一写入不同的分割子文件
        for (; i < file_num; i++) {
            FILE *fpw = fopen(patches[i], "wb");
            int j = 0;
            for (; j < part; j++) {
                //边读边写
                fputc(fgetc(fpr), fpw);
            }
            fclose(fpw);
        }
    }
    else {
        //不整除
        int part = filesize / (file_num - 1);
        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);
        }
        //the last one
        FILE *fpw = fopen(patches[file_num - 1], "wb");
        i = 0;
        for (; i < filesize % (file_num - 1); i++) {
            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);

}


JNIEXPORT void JNICALL Java_com_haocai_ndktest_NDKFileUtils_patch
(JNIEnv * env, jclass jcls, jstring path_pattern_jstr, jint file_num, jstring merge_path_jstr) {

    //合并之后的文件
    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 = 0; i < file_num; i++) {
        patches[i] = malloc(sizeof(char) * 100);
        //元素复制
        //需要分割的文件:C://jason/
        sprintf(patches[i], path_pattern, (i + 1));
        LOGI("patch path:%s", patches[i]);
    }
    FILE *fpw = fopen(merge_path, "wb");
    //把所有的分割文件读取一遍,写入一个总的文件中
    i = 0;
    for (; i < file_num; i++) {
        //每个字文件的大小
        long filesize = get_file_size(patches[i]);
        FILE *fpr = fopen(patches[i], "rb");
        int j = 0;
        for (; j < filesize; j++) {
            fputc(fgetc(fpr), fpw);
        }
        fclose(fpr);

    }
    fclose(fpw);

    //释放资源
    (*env)->ReleaseStringUTFChars(env, path_pattern_jstr, path_pattern);
    (*env)->ReleaseStringUTFChars(env, merge_path_jstr, merge_path);
}

4.写mk文件

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := mylibrary
LOCAL_SRC_FILES =: mylibrary.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

Applicatoin.mk

APP_MODULES := mylibrary
APP_ABI := armeabi-v7a armeabi arm64-v8a  #定义编译目标版本 ABI(应用二进制接口)  值为all时,代表所有版本
APP_PLATFORM := android-14

5.写Android调用库主程序

package com.haocai.ndktest;

import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import java.io.File;

public class MainActivity extends AppCompatActivity {

    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();
    }

    /**
     * 拆分包
     *
     * @param v
     */
    public void diffPackage(View v) {
        String path = SD_CARD_PATH + File.separator+ "小苹果.mp3";
        String path_pattern = SD_CARD_PATH + File.separator +"小苹果_%d.mp3";
        NDKFileUtils.diff(path, path_pattern,3);
        Log.d("Main","拆分成功");
    }

    /**
     * 合并包
     *
     * @param v
     */
    public void patchPackage(View v) {
        String merge_path = SD_CARD_PATH + File.separator+ "小苹果_合并.mp3";
        String path_pattern = SD_CARD_PATH + File.separator +"小苹果_%d.mp3";
        NDKFileUtils.patch(path_pattern, 3,merge_path);
        Log.d("Main","合并成功");
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    tools:context="com.haocai.ndktest.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拆分包"
        android:onClick="diffPackage"
        />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="合并包"
        android:onClick="patchPackage"
        />
</LinearLayout>

权限声明

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

6.运行结果

1.要把小苹果.mp3文件放在Android手机sd目录下
2.运行拆分包程序
生成 小苹果_1.mp3 小苹果_2.mp3 小苹果_3.mp3
3.运行合并包程序
生成 小苹果_合并.mp3

如下:

11-13 15:17:01.076 2648-2648/com.haocai.ndktest I/kpioneer: patch path:/storage/emulated/0/小苹果_1.mp3
11-13 15:17:01.076 2648-2648/com.haocai.ndktest I/kpioneer: patch path:/storage/emulated/0/小苹果_2.mp3
11-13 15:17:01.076 2648-2648/com.haocai.ndktest I/kpioneer: patch path:/storage/emulated/0/小苹果_3.mp3
11-13 15:17:02.016 2648-2648/com.haocai.ndktest D/Main: 拆分成功
11-13 15:17:02.026 2648-2648/com.haocai.ndktest I/Choreographer: Skipped 56 frames!  The application may be doing too much work on its main thread.
11-13 15:17:03.226 2648-2648/com.haocai.ndktest D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
11-13 15:17:03.286 2648-2648/com.haocai.ndktest I/kpioneer: patch path:/storage/emulated/0/小苹果_1.mp3
11-13 15:17:03.286 2648-2648/com.haocai.ndktest I/kpioneer: patch path:/storage/emulated/0/小苹果_2.mp3
11-13 15:17:03.286 2648-2648/com.haocai.ndktest I/kpioneer: patch path:/storage/emulated/0/小苹果_3.mp3
11-13 15:17:04.256 2648-2648/com.haocai.ndktest D/Main: 合并成功
先拆分后合并
该项目源码下载

特别感谢:
动脑学院Jason







微信号kpioneer

推荐阅读更多精彩内容