版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
一种文件压缩格式,具有高压缩比率,进行数据压缩有多种压缩算法可以选择。与其它压缩格式相比,得到的压缩文档较小,即压缩率最高,节省磁盘空间。
对 7z 的压缩格式,我们可以使用 7-Zip,下载地址。
完全免费而且开源的压缩软件,相比其他软件有更高的压缩比但同时耗费的资源也相对更多。支持压缩/ 解压缩:7z, XZ, BZIP2, GZIP, TAR, ZIP, WIM。
压缩文件尺寸:
压缩得分:
压缩时间:
整体来说,7-Zip 的性能相对较好。
0 不压缩
1 快速压缩
5 正常压缩
7 最大压缩
9 极限压缩
7z a [输出文件] [待压缩文件/目录] -mx=9
-t7z 压缩文件的格式为7z(压缩zip则为-tzip)
-mx=9 设置压缩等级为极限压缩
7z x [压缩文件] -o[输出目录]
要在安卓中进行使用,我们需要先下载 7zip 的源码。下载地址。
也可以使用命令行进行下载:
wget https://jaist.dl.sourceforge.net/project/p7zip/p7zip/16.02/p7zip_16.02_src_all.tar.bz2
下载下来的是一个压缩包,进行解压查看。
Runtime.getRuntime().exec(“xxx”)
7z 的使用不需要对执行过程进行干预,也就是不需要在执行过程中操作数据,只在乎最后得到一个 7z 文件或者解压出 7z 文件。因此可以使用命令行来使用 7zip 压缩与解压。(同理对于视频文件的压缩、转换也可以使用ffmpeg 命令行,但是对于实时编码摄像头数据就必须编码完成)
首先,我们需要在安卓手机中拥有 7zip 的可执行文件,类似 window 中的 7zip.exe。
进入刚才下载压缩包下的目录, ./CPP/ANDROID/7zr
7z 使用了插件,能进行更多的格式支持(能支持 tar、zip 等)
7za 只是用7zip
7zr 只支持7z格式
我们只需要 7z 的格式,所以这边我们采用的是 7zr,这是最精简的。(如果要使用 7za 的话也是可以,编译步骤一样)
我们进入 7zr 目录,查看目录结构。
里面有个 makefile 文件,这个是在 eclipse 创建安卓项目时候使用,我们打开 makefile 文件进行查看,里面就有编译步骤。
进到 jni 目录进行查看,是两个 .mk 文件。
查看 Application.mk 文件。里面就是指定对应的平台,默认只有 armeabi,我们可以在这里进行对对应平台的添加。
# The ARMv7 is significanly faster due to the use of the hardware FPU
APP_ABI := armeabi
# p7zip armeabi and armeabi-v7a run at the same speed (p7zip does not use FPU)
# APP_ABI := armeabi armeabi-v7a
#APP_PLATFORM := android-8
修改为:
# The ARMv7 is significanly faster due to the use of the hardware FPU
APP_ABI := armeabi-v7a x86
# p7zip armeabi and armeabi-v7a run at the same speed (p7zip does not use FPU)
# APP_ABI := armeabi armeabi-v7a
#APP_PLATFORM := android-8
在 jni 目录下按 makefile 文件中写的,运行 ndk-build 。
注: NDK 的版本要使用旧的版本,否者会报错。这边个人是使用 ndk 12 进行编译,下载地址。(ndk 15 太新,编译出错,ndk 10 编译过得去,在项目运行时候 cmake 报错)
先把生成的可执行文件拷贝到 assets 目录下,打包进 apk。
运行 7zip 前,先把可执行文件拷贝到安卓系统中。
/**
* 把 7z 拷贝到手机
* @param view
*/
public void load(View view) {
File diskFilename = new File(getFilesDir(), "7zr");
//不存在,则进行拷贝
if (!diskFilename.exists()) {
String assetFilename;
//根据cpu 拷贝不同的可执行文件
if (Build.CPU_ABI.startsWith("armeabi") ||
Build.CPU_ABI.equals("x86")) {
assetFilename = "libs/" + Build.CPU_ABI + "/7zr";
} else {
Toast.makeText(this, "加载错误,平台不符", Toast.LENGTH_SHORT).show();
return;
}
AssetsFileUtil.copyFromAssets(this, assetFilename, diskFilename.getAbsolutePath());
}
//存在但不能执行
if (!diskFilename.canExecute()) {
//设置可执行并
diskFilename.setExecutable(true);
}
Toast.makeText(this, "加载7zr 结果:"
+ (diskFilename.exists() && diskFilename.canExecute()),
Toast.LENGTH_SHORT).show();
}
然后使用代码进行 7zip 压缩和解压命令的执行。具体 demo 见末尾。
**注:**这边是对 sdcard 目录下的 7-Zip 文件夹进行压缩,所以在运行前需保证改文件夹存在。
在上面的配置基础上,配置编译成动态库。
修改 Android.mk 文件末尾,去除配置 pie,使用默认的。设置编译内容为动态库。
Android.mk:
# Needed since ANDROID 5, these programs run on android-16 (Android 4.1+)
# pie 是给可执行程序使用的 flag
# LOCAL_CFLAGS += -fPIE
# LOCAL_LDFLAGS += -fPIE -pie
include $(BUILD_SHARED_LIBRARY)
# BUILD_EXECUTABLE 可执行文件
# BUILD_SHARED_LIBRARY 动态库
# BUILD_STATIC_LIBRARY 静态库
与编译成执行文件一样的操作步骤,进行编译,等待一会,即可在 libs 下面生产对应的动态库。
新建 cpp/libs 文件夹,然后把生成的动态库拷贝进来。
在 app 下的 build.gradle 中添加 so 包的引用路径。(如果把 so 放在默认 jniLibs 文件夹下则不需要进行添加,也是可以的。)
android {
...
sourceSets {
main {
jniLibs.srcDirs = ['src/main/cpp/libs']
}
}
}
在 app 的 build.gradle 中进行添加以下配置:
android {
compileSdkVersion 28
defaultConfig {
......
externalNativeBuild {
cmake {
cppFlags ""
abiFilters 'armeabi-v7a','x86'
}
}
}
}
在 app 的 build.gradle 中添加 CMakeLists 路径。
android {
......
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
在 app 下创建 CMakeLists.txt 文件。
CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
set(LIB_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp/libs/${ANDROID_ABI})
add_library(lib7zr SHARED IMPORTED)
set_target_properties(lib7zr PROPERTIES IMPORTED_LOCATION ${LIB_DIR}/lib7zr.so)
#根据Android.mk引入头文件
#设置头文件查找目录
include_directories(src/main/cpp/lib7zr/CPP/7zip/Archive
src/main/cpp/lib7zr/CPP/7zip/Archive/7z
src/main/cpp/lib7zr/CPP/7zip/Archive/BZip2
src/main/cpp/lib7zr/CPP/7zip/Archive/Common
src/main/cpp/lib7zr/CPP/7zip/Archive/GZip
src/main/cpp/lib7zr/CPP/7zip/Archive/Cab
src/main/cpp/lib7zr/CPP/7zip/Archive/Lzma
src/main/cpp/lib7zr/CPP/7zip/Archive/Tar
src/main/cpp/lib7zr/CPP/7zip/Archive/Zip
src/main/cpp/lib7zr/CPP/7zip/Archive/Split
src/main/cpp/lib7zr/CPP/7zip/Archive/Z
src/main/cpp/lib7zr/CPP/7zip/Compress
src/main/cpp/lib7zr/CPP/7zip/Crypto
src/main/cpp/lib7zr/CPP/7zip/UI/Console
src/main/cpp/lib7zr/CPP/7zip/UI/Common
src/main/cpp/lib7zr/CPP/Windows
src/main/cpp/lib7zr/CPP/Common
src/main/cpp/lib7zr/CPP/7zip/Common
src/main/cpp/lib7zr/C
src/main/cpp/lib7zr/CPP/myWindows
src/main/cpp/lib7zr/CPP
src/main/cpp/lib7zr/CPP/include_windows)
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp
)
target_link_libraries(
native-lib
lib7zr
log )
这个 CMakeLists.txt 编写是模仿 7zip 进行编写的。查看 Android.mk,把引用到的头文件拷贝进来就是了。
上面的 CMakeLists.txt 使用到了一些 7zip 的文件,我们需要把这些文件拷贝到 app/src/main/cpp/lib7zr 下。
在 app/src/main/cpp 下创建 native-lib.cpp 文件,进行 C 代码的编写。
native-lib:
#include
#include
#include <7zTypes.h>
//
#include
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "7zr",__VA_ARGS__);
//c++与c兼容
// int a(int x) i_ai
// i_a
//表示这个函数在别的地方实现
extern int MY_CDECL main
(
#ifndef _WIN32
int numArgs, char *args[]
#endif
);
void strArgs(const char *cmd, int &args, char pString[66][1024]);
extern "C"
JNIEXPORT jint JNICALL
Java_com_xiaoyue_project7zip_ZipCode_exec(JNIEnv *env, jclass type, jstring cmd_) {
const char *cmd = env->GetStringUTFChars(cmd_, 0);
//7zr a /sdcard/7-Zip.7z /sdcard/7-Zip -mx=9
int numArgs;
char temp[66][1024] = {0};
//分割字符串 将值填入变量
strArgs(cmd, numArgs, temp);
char *args[] = {0};
for (int i = 0; i < numArgs; ++i) {
args[i] = temp[i];
LOGE("%s", args[i]);
}
env->ReleaseStringUTFChars(cmd_, cmd);
return main(numArgs,args);
}
void strArgs(const char *cmd, int &numArgs, char argv[66][1024]) {
//获得字符串长度
int size = strlen(cmd);
//argv的两个下标
int a = 0, b = 0;
//0 = false
//记录是否进入空格
//7zr a /sdcard/7-Zip.7z /sdcard/7-Zip -mx=9
//7zr\0
int inspace = 0;
for (int i = 0; i < size; ++i) {
char c = cmd[i];
switch (c) {
case ' ':
case '\t':
if (inspace) {
//字符串结束符号
argv[a][b++] = '\0';
a++;
//加入下一个有效字符前 复原
b = 0;
inspace = 0;
}
break;
default:
//如果是字符
inspace = 1;
argv[a][b++] = c;
break;
}
}
//7zr a /sdcard/7-Zip.7z /sdcard/7-Zip -mx=9
//如果最末尾不是空格 就不会进入 case ' ': case '\t': 补上最后一个结束符
//if(inspace){}
if (cmd[size - 1] != ' ' && cmd[size - 1] != '\t') {
argv[a][b] = '\0';
a++;
}
numArgs = a;
}
同时编写一个 java 类 ZipCode 进行加载 native-lib。
ZipCode:
public class ZipCode {
static {
System.loadLibrary("native-lib");
}
//7zr a xxx.7z xx
public native static int exec(String cmd);
}
在 MainActivity 中进行代码的调用。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File src = new File(Environment.getExternalStorageDirectory(), "7-Zip");
File out = new File(Environment.getExternalStorageDirectory(), "7-Zip.7z");
File out2 = new File(Environment.getExternalStorageDirectory(), "7-Zip_code");
ZipCode.exec("7zr a " + out.getAbsolutePath() + " " + src.getAbsolutePath() + " -mx=9");
ZipCode.exec("7zr x " + out.getAbsolutePath() + " -o" + out2.getAbsolutePath());
}
注: 这边是对sdcard 目录下的 7-Zip 文件夹进行压缩,所以在运行前需保证改文件夹存在。
在 sdcard 目录下生成 7-Zip.7z 压缩包和 7-Zip_code 解压文件夹。
可以使用 7-Zip 对上传服务的文件进行压缩,减小体积。也可以使用 7-Zip 对 dex 文件进行压缩,减小 apk 的体积。
代码链接