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
/* 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
#include
#include
#include
#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
权限声明
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