(十九)7-Zip 压缩

版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、概述

1.7z

一种文件压缩格式,具有高压缩比率,进行数据压缩有多种压缩算法可以选择。与其它压缩格式相比,得到的压缩文档较小,即压缩率最高,节省磁盘空间。

2.7-Zip

对 7z 的压缩格式,我们可以使用 7-Zip,下载地址。

完全免费而且开源的压缩软件,相比其他软件有更高的压缩比但同时耗费的资源也相对更多。支持压缩/ 解压缩:7z, XZ, BZIP2, GZIP, TAR, ZIP, WIM。

3.数据

压缩率:
(十九)7-Zip 压缩_第1张图片

压缩文件尺寸:
(十九)7-Zip 压缩_第2张图片
压缩得分:
(十九)7-Zip 压缩_第3张图片
压缩时间:
(十九)7-Zip 压缩_第4张图片
整体来说,7-Zip 的性能相对较好。

二、7zip 的使用

1.压缩等级

0	不压缩
1	快速压缩
5	正常压缩
7	最大压缩
9	极限压缩

2.压缩命令

7z a   [输出文件] [待压缩文件/目录] -mx=9 

在这里插入图片描述

-t7z 	压缩文件的格式为7z(压缩zip则为-tzip)
-mx=9	设置压缩等级为极限压缩

3.解压命令

7z x [压缩文件]  -o[输出目录]

在这里插入图片描述

三、命令行使用 7z

1.下载源码

要在安卓中进行使用,我们需要先下载 7zip 的源码。下载地址。
(十九)7-Zip 压缩_第5张图片

也可以使用命令行进行下载:

wget  https://jaist.dl.sourceforge.net/project/p7zip/p7zip/16.02/p7zip_16.02_src_all.tar.bz2

下载下来的是一个压缩包,进行解压查看。

2.选取格式

Runtime.getRuntime().exec(“xxx”)

7z 的使用不需要对执行过程进行干预,也就是不需要在执行过程中操作数据,只在乎最后得到一个 7z 文件或者解压出 7z 文件。因此可以使用命令行来使用 7zip 压缩与解压。(同理对于视频文件的压缩、转换也可以使用ffmpeg 命令行,但是对于实时编码摄像头数据就必须编码完成)

首先,我们需要在安卓手机中拥有 7zip 的可执行文件,类似 window 中的 7zip.exe。

进入刚才下载压缩包下的目录, ./CPP/ANDROID/7zr
(十九)7-Zip 压缩_第6张图片

7z	 使用了插件,能进行更多的格式支持(能支持 tar、zip 等)
7za	 只是用7zip
7zr  只支持7z格式

我们只需要 7z 的格式,所以这边我们采用的是 7zr,这是最精简的。(如果要使用 7za 的话也是可以,编译步骤一样)

3.编译

我们进入 7zr 目录,查看目录结构。
(十九)7-Zip 压缩_第7张图片
里面有个 makefile 文件,这个是在 eclipse 创建安卓项目时候使用,我们打开 makefile 文件进行查看,里面就有编译步骤。
(十九)7-Zip 压缩_第8张图片
进到 jni 目录进行查看,是两个 .mk 文件。
(十九)7-Zip 压缩_第9张图片

查看 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 报错)

编译成功后的目录:
(十九)7-Zip 压缩_第10张图片

4.使用

先把生成的可执行文件拷贝到 assets 目录下,打包进 apk。
(十九)7-Zip 压缩_第11张图片
运行 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 文件夹进行压缩,所以在运行前需保证改文件夹存在。

四、NDK 使用 7z

1.设置编译动态库

在上面的配置基础上,配置编译成动态库。

修改 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 静态库

2.编译

与编译成执行文件一样的操作步骤,进行编译,等待一会,即可在 libs 下面生产对应的动态库。

3.添加动态库

新建 cpp/libs 文件夹,然后把生成的动态库拷贝进来。
(十九)7-Zip 压缩_第12张图片
在 app 下的 build.gradle 中添加 so 包的引用路径。(如果把 so 放在默认 jniLibs 文件夹下则不需要进行添加,也是可以的。)

android {
    ...
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/cpp/libs']
        }
    }
}

4.配置 NDK 运行的平台

在 app 的 build.gradle 中进行添加以下配置:

android {
    compileSdkVersion 28
    defaultConfig {
		......
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters 'armeabi-v7a','x86'
            }
        }
    }
}

5.配置 CMakeLists

在 app 的 build.gradle 中添加 CMakeLists 路径。

android {
	......
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

在 app 下创建 CMakeLists.txt 文件。
(十九)7-Zip 压缩_第13张图片
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 下。

7zip 下文件:
(十九)7-Zip 压缩_第14张图片

拷贝后文件:
(十九)7-Zip 压缩_第15张图片

6.编写 C 代码

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

7.测试

在 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 文件夹进行压缩,所以在运行前需保证改文件夹存在。

8.运行结果

在 sdcard 目录下生成 7-Zip.7z 压缩包和 7-Zip_code 解压文件夹。

五、使用场景

可以使用 7-Zip 对上传服务的文件进行压缩,减小体积。也可以使用 7-Zip 对 dex 文件进行压缩,减小 apk 的体积。

六、附

代码链接

你可能感兴趣的:(性能优化)