【Android 安装包优化】使用 lib7zr.so 动态库处理压缩文件 ( jni 中 main 函数声明 | 命令行处理 | jni 调用 lib7zr.so 函数库处理压缩文件完整代码 )
文章目錄
- 一、JNI 中 main 函數聲明
- 二、命令字符串切割并傳入 main 函數
- 三、完整代碼示例
- 1、完整 jni 代碼
- 2、完整 java 代碼
- 3、執行結果
- 四、參考資料
前置博客 :
- 【Android 安裝包優化】使用 lib7zr.so 動態庫處理壓縮文件 ( 修改 7zr 交叉編譯腳本 Android.mk | 交叉編譯 lib7zr.so 動態庫 )
- 【Android 安裝包優化】使用 lib7zr.so 動態庫處理壓縮文件 ( 拷貝 lib7zr.so 動態庫到 Android Studio 工程 | 配置 build.gradle 構建腳本 )
- 【Android 安裝包優化】使用 lib7zr.so 動態庫處理壓縮文件 ( 拷貝 lib7zr.so 動態庫頭文件到 Android 工程中 | 配置 CMakeLists.txt 構建腳本 )
- 【Android 安裝包優化】使用 lib7zr.so 動態庫處理壓縮文件 ( 測試 lib7zr.so 動態庫調用 )
一、JNI 中 main 函數聲明
使用 7zr 可執行程序處理壓縮文件時時 , 調用的是其主函數 , CPP\7zip\UI\Console\MainAr.cpp 中的 main 函數 , 傳入 7z a outputFile inputFile -mx=compressDegree -tcompressType 壓縮命令 , 或 7z x [輸入文件] -o[輸出目錄] 解壓命令 , 都是使用該主函數接收相關參數 ;
int MY_CDECL main (#ifndef _WIN32int numArgs, char *args[]#endif )int numArgs 參數表示字符串個數 ;
如 7zr a files.7z files -mx=9 -t7z 命令中 , 有 666 個字符串 , 由 555 個空格隔開 ;
char *args[] 是 指針數組 , 數組中的元素是 char * 類型的指針 , 就是字符串 , 這是個字符串數組 ;
7zr 程序中的主要的頭文件是 7zTypes.h , 該頭文件中 聲明了主要的 類型 和 函數 ; 引入該頭文件 ;
#include <7zTypes.h>聲明外部函數 :
// 表示該函數在其它代碼中實現 // 這是 CPP\7zip\UI\Console\MainAr.cpp 中的 main 函數 extern int MY_CDECL main( #ifndef _WIN32int numArgs, char *args[] #endif );點擊聲明左側的雙向箭頭按鈕 , 可以跳轉到 MainAr.cpp 的 main 函數位置 ;
跳轉位置 :
二、命令字符串切割并傳入 main 函數
調用 main 函數 , 需要傳入對應的參數 , 分別是
-
int numArgs 字符串個數 ;
-
char *args[] 字符串數組 ; 指針數組 , 每個數組元素中都有一個 char * 指針元素 , 指向字符串 ;
從 Java 傳入 C 的指令如下 :
7zr a files.7z files -mx=9 -t7z使用空格將上述指令切割成 666 個字符串 , 然后傳入 main 函數 ;
字符串切割過程 :
// 命令示例 : 7zr a files.7z files -mx=9 -t7z// 參數個數int argCount = 0;// 存放多個字符串, 最多 20 個字符串 , 每個最多 1024 個字符char argArray[20][1024] = {0};//分割字符串 將值填入變量// 獲取 cmd_java 字符串長度int cmd_size = strlen(cmd_java);// 二維數組 循環控制變量, 第一個是字符串數組 , 第二個是字符串中的字符數組int str_index = 0, char_index = 0;// 標記字符是否是非空字符, tab, 如果是則設置 1 , 如果不是設置 0// 開始時默認 0, 不是空格int isChar = 0;// 逐個字節遍歷 字符for(int i = 0; i < cmd_size; i ++){// 獲取一個字符char c = cmd_java[i];//LOGI("遍歷 %d . %c , cmd_size = %d", i, c, cmd_size);switch (c) {case ' ': // 判斷是否是空格case '\t': // 判斷是否是 TAB 空格if(isChar){// 如果上一個字符不是空格 , 則需要結束當前的字符串argArray[str_index][char_index++] = '\0';// 字符串索引自增 1str_index ++;// 字符串內字符索引歸零char_index = 0;// 設置當前的字符為空格標志位 1isChar = 0;}else{// 如果之前是空格, 那么現在也是空格 ,// 說明命令中有多個空格 , 此處不做任何處理}break;default:isChar = 1;// 將當前字符放入數組中argArray[str_index][char_index++] = c;break;}}// 如果最后一位不是空格 , 則需要手動將最后一個字符串寫入到數組中if (cmd_java[cmd_size - 1] != ' ' && cmd_java[cmd_size - 1] != '\t') {// 如果上一個字符不是空格 , 則需要結束當前的字符串argArray[str_index][char_index++] = '\0';// 字符串索引自增 1str_index ++;}// 統計字符串個數argCount = str_index;// 拼裝字符串數組char *args[] = {0};for (int i = 0; i < argCount; ++i) {args[i] = argArray[i];// 打印字符串數組LOGI("%d . %s", i, args[i]);}最終的字符串個數是 argCount , 字符串數組 args ;
將這兩個參數傳入 main 函數即可 ;
三、完整代碼示例
1、完整 jni 代碼
完整 jni 層 C++ 代碼如下 :
#include <jni.h> #include <string> #include <7zTypes.h> #include "logging_macros.h"// 表示該函數在其它代碼中實現 // 這是 CPP\7zip\UI\Console\MainAr.cpp 中的 main 函數 extern int MY_CDECL main( #ifndef _WIN32int numArgs, char *args[] #endif );extern "C" JNIEXPORT void JNICALL Java_kim_hsl_a7_1zip_MainActivity_executeCmd(JNIEnv* env, jobject thiz, jstring cmd) {// 將 Java 字符串轉為 C 字符串const char *cmd_java = env->GetStringUTFChars(cmd, 0);LOGI("jni 中處理壓縮文件命令 : %s", cmd_java);// 命令示例 : 7zr a files.7z files -mx=9 -t7z// 參數個數int argCount = 0;// 存放多個字符串, 最多 20 個字符串 , 每個最多 1024 個字符char argArray[20][1024] = {0};//分割字符串 將值填入變量// 獲取 cmd_java 字符串長度int cmd_size = strlen(cmd_java);// 二維數組 循環控制變量, 第一個是字符串數組 , 第二個是字符串中的字符數組int str_index = 0, char_index = 0;// 標記字符是否是非空字符, tab, 如果是則設置 1 , 如果不是設置 0// 開始時默認 0, 不是空格int isChar = 0;// 逐個字節遍歷 字符for(int i = 0; i < cmd_size; i ++){// 獲取一個字符char c = cmd_java[i];//LOGI("遍歷 %d . %c , cmd_size = %d", i, c, cmd_size);switch (c) {case ' ': // 判斷是否是空格case '\t': // 判斷是否是 TAB 空格if(isChar){// 如果上一個字符不是空格 , 則需要結束當前的字符串argArray[str_index][char_index++] = '\0';// 字符串索引自增 1str_index ++;// 字符串內字符索引歸零char_index = 0;// 設置當前的字符為空格標志位 1isChar = 0;}else{// 如果之前是空格, 那么現在也是空格 ,// 說明命令中有多個空格 , 此處不做任何處理}break;default:isChar = 1;// 將當前字符放入數組中argArray[str_index][char_index++] = c;break;}}// 如果最后一位不是空格 , 則需要手動將最后一個字符串寫入到數組中if (cmd_java[cmd_size - 1] != ' ' && cmd_java[cmd_size - 1] != '\t') {// 如果上一個字符不是空格 , 則需要結束當前的字符串argArray[str_index][char_index++] = '\0';// 字符串索引自增 1str_index ++;}// 統計字符串個數argCount = str_index;// 拼裝字符串數組char *args[] = {0};for (int i = 0; i < argCount; ++i) {args[i] = argArray[i];// 打印字符串數組LOGI("%d . %s", i, args[i]);}// 量參數傳入 main 函數main(argCount, args);// 釋放 Java 字符串以及 C 字符串env->ReleaseStringUTFChars(cmd, cmd_java);LOGI("7zr 命令執行完畢 !"); }2、完整 java 代碼
package kim.hsl.a7_zipimport android.os.Build import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import java.io.*class MainActivity : AppCompatActivity() {companion object {val TAG = "MainActivity"init {System.loadLibrary("native-lib")}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)copy7zr()compress7z()uncompress7z()compress7zJni()}/*** 將 7zr 文件拷貝到應用私有目錄*/fun copy7zr() {Log.i(TAG, "開始拷貝 7zr 文件")// /data/user/0/kim.hsl.a7_zip/files/7zrvar exeFile = File(filesDir, "7zr")Log.i(TAG, "filesDir = ${filesDir.absolutePath} , exeFile = ${exeFile.absolutePath}")// 查看該文件是否存在, 如果存在設置該文件可執行// 如果不存在 , 拷貝文件if (exeFile.exists()) {exeFile.setExecutable(true)Log.i(TAG, "內置存儲空間存在該 /data/user/0/kim.hsl.a7_zip/files/7zr 文件")return} else {Log.i(TAG, "內置存儲空間不存在 7zr 可執行文件 , 開始拷貝文件")}// 如果不存在 , 拷貝文件var inputStream: InputStream = assets.open("libs/arm64-v8a/7zr")// /data/user/0/kim.hsl.a7_zip/files/7zrvar fileOutputStream: FileOutputStream = FileOutputStream(exeFile)Log.i(TAG, "Build.CPU_ABI = ${Build.CPU_ABI}")// 不同 CPU 架構拷貝不同的可執行程序if (Build.CPU_ABI.startsWith("armeabi-v7a")) {inputStream = assets.open("libs/armeabi-v7a/7zr")} else if (Build.CPU_ABI.startsWith("arm64-v8a")) {inputStream = assets.open("libs/arm64-v8a/7zr")} else if (Build.CPU_ABI.startsWith("x86")) {inputStream = assets.open("libs/x86/7zr")} else if (Build.CPU_ABI.startsWith("x86_64")) {inputStream = assets.open("libs/x86_64/7zr")}// 拷貝文件var buffer: ByteArray = ByteArray(1024)var readCount = inputStream.read(buffer);while (readCount != -1) {fileOutputStream.write(buffer)readCount = inputStream.read(buffer);}fileOutputStream.flush()fileOutputStream.close()Log.i(TAG, "拷貝 7zr 文件結束")}/*** 使用 7zr 進行壓縮*/fun compress7z() {// /data/user/0/kim.hsl.a7_zip/files/7zrvar exeFile = File(filesDir, "7zr")// 執行前賦予可執行權限exeFile.setExecutable(true)var file_7z = File("${filesDir.absolutePath}/files.7z")if(file_7z.exists()){file_7z.delete()}var cmd = "${exeFile.absolutePath} a ${filesDir.absolutePath}/files.7z ${filesDir.absolutePath} -mx=9 -t7z"Log.i(TAG, "壓縮命令 : $cmd")var process: Process = Runtime.getRuntime().exec(cmd)// 讀取命令執行過程數據var reader = BufferedReader(InputStreamReader(process.inputStream))while (true) {val line = reader.readLine()if (line != null) {Log.i(TAG, "$line")}else{break}}reader.close()val exitValue = process.exitValue()Log.i(TAG, "壓縮文件 , 執行完畢 , exitValue = $exitValue")}/*** 判定命令是否執行完畢* 調用 process.exitValue 方法 , 如果沒有執行完畢 , 會拋異常,* 如果執行完畢會返回一個確定的值*/fun isComplete(process: Process): Boolean {try {// 已經執行完畢process.exitValue()return true} catch (e: IllegalThreadStateException) {// 未執行完畢return false}}/*** 使用 7zr 進行解壓縮*/fun uncompress7z() {// /data/user/0/kim.hsl.a7_zip/files/7zrvar exeFile = File(filesDir, "7zr")// 執行前賦予可執行權限exeFile.setExecutable(true)// 刪除解壓目錄var unzip_file = File("${filesDir.absolutePath}/unzip_file")if(unzip_file.exists()){recursionDeleteFile(unzip_file)}var cmd = "${exeFile.absolutePath} x ${filesDir.absolutePath}/files.7z -o${filesDir.absolutePath}/unzip_file"Log.i(TAG, "解壓縮命令 : $cmd")var process: Process = Runtime.getRuntime().exec(cmd)// 讀取命令執行過程數據var reader = BufferedReader(InputStreamReader(process.inputStream))while (true) {val line = reader.readLine()if (line != null) {Log.i(TAG, "$line")}else{break}}reader.close()val exitValue = process.exitValue()Log.i(TAG, "解壓縮文件 , 執行完畢 , exitValue = $exitValue")}/*** 遞歸刪除文件*/fun recursionDeleteFile(file: File) {if (file.isDirectory) {// 如果是目錄 , 則遞歸刪除file.listFiles().forEach {// ForEach 循環刪除目錄recursionDeleteFile(it)}} else {// 如果是文件直接刪除file.delete()}}/*** 使用 7zr 進行壓縮*/fun compress7zJni() {// /data/user/0/kim.hsl.a7_zip/files/7zrvar exeFile = File(filesDir, "7zr")// 執行前賦予可執行權限exeFile.setExecutable(true)// 刪除原有的壓縮文件, 如果存在var file_7z = File("${filesDir.absolutePath}/files_jni.7z")if(file_7z.exists()){file_7z.delete()}var cmd = "${exeFile.absolutePath} a ${filesDir.absolutePath}/files_jni.7z ${filesDir.absolutePath} -mx=9 -t7z"Log.i(TAG, "Jni 壓縮命令 : $cmd")// 調用 jni 方法處理壓縮文件executeCmd(cmd)}external fun executeCmd(cmd: String): Unit }
3、執行結果
2021-05-07 13:32:12.520 31022-31022/kim.hsl.a7_zip I/MainActivity: 開始拷貝 7zr 文件 2021-05-07 13:32:12.524 31022-31022/kim.hsl.a7_zip I/MainActivity: filesDir = /data/user/0/kim.hsl.a7_zip/files , exeFile = /data/user/0/kim.hsl.a7_zip/files/7zr 2021-05-07 13:32:12.525 31022-31022/kim.hsl.a7_zip I/MainActivity: 內置存儲空間存在該 /data/user/0/kim.hsl.a7_zip/files/7zr 文件 2021-05-07 13:32:12.527 31022-31022/kim.hsl.a7_zip I/MainActivity: 壓縮命令 : /data/user/0/kim.hsl.a7_zip/files/7zr a /data/user/0/kim.hsl.a7_zip/files/files.7z /data/user/0/kim.hsl.a7_zip/files -mx=9 -t7z 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: 7-Zip (a) [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,8 CPUs LE) 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: Scanning the drive: 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: 11 folders, 8 files, 7403414 bytes (7230 KiB) 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: Creating archive: /data/user/0/kim.hsl.a7_zip/files/files.7z 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: Items to compress: 19 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: Files read from disk: 8 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: Archive size: 941905 bytes (920 KiB) 2021-05-07 13:32:19.027 31022-31022/kim.hsl.a7_zip I/MainActivity: Everything is Ok 2021-05-07 13:32:19.028 31022-31022/kim.hsl.a7_zip I/MainActivity: 壓縮文件 , 執行完畢 , exitValue = 0 2021-05-07 13:32:19.036 31022-31022/kim.hsl.a7_zip I/MainActivity: 解壓縮命令 : /data/user/0/kim.hsl.a7_zip/files/7zr x /data/user/0/kim.hsl.a7_zip/files/files.7z -o/data/user/0/kim.hsl.a7_zip/files/unzip_file 2021-05-07 13:32:19.059 31022-31022/kim.hsl.a7_zip I/MainActivity: 7-Zip (a) [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 2021-05-07 13:32:19.059 31022-31022/kim.hsl.a7_zip I/MainActivity: p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,8 CPUs LE) 2021-05-07 13:32:19.059 31022-31022/kim.hsl.a7_zip I/MainActivity: Scanning the drive for archives: 2021-05-07 13:32:19.059 31022-31022/kim.hsl.a7_zip I/MainActivity: 1 file, 941905 bytes (920 KiB) 2021-05-07 13:32:19.059 31022-31022/kim.hsl.a7_zip I/MainActivity: Extracting archive: /data/user/0/kim.hsl.a7_zip/files/files.7z 2021-05-07 13:32:19.185 31022-31022/kim.hsl.a7_zip I/MainActivity: -- 2021-05-07 13:32:19.185 31022-31022/kim.hsl.a7_zip I/MainActivity: Path = /data/user/0/kim.hsl.a7_zip/files/files.7z 2021-05-07 13:32:19.185 31022-31022/kim.hsl.a7_zip I/MainActivity: Type = 7z 2021-05-07 13:32:19.185 31022-31022/kim.hsl.a7_zip I/MainActivity: Physical Size = 941905 2021-05-07 13:32:19.185 31022-31022/kim.hsl.a7_zip I/MainActivity: Headers Size = 333 2021-05-07 13:32:19.185 31022-31022/kim.hsl.a7_zip I/MainActivity: Method = LZMA2:23 2021-05-07 13:32:19.186 31022-31022/kim.hsl.a7_zip I/MainActivity: Solid = + 2021-05-07 13:32:19.186 31022-31022/kim.hsl.a7_zip I/MainActivity: Blocks = 1 2021-05-07 13:32:19.186 31022-31022/kim.hsl.a7_zip I/MainActivity: Everything is Ok 2021-05-07 13:32:19.186 31022-31022/kim.hsl.a7_zip I/MainActivity: Folders: 11 2021-05-07 13:32:19.186 31022-31022/kim.hsl.a7_zip I/MainActivity: Files: 8 2021-05-07 13:32:19.186 31022-31022/kim.hsl.a7_zip I/MainActivity: Size: 7403414 2021-05-07 13:32:19.186 31022-31022/kim.hsl.a7_zip I/MainActivity: Compressed: 941905 2021-05-07 13:32:19.186 31022-31022/kim.hsl.a7_zip I/MainActivity: 解壓縮文件 , 執行完畢 , exitValue = 0 2021-05-07 13:32:19.187 31022-31022/kim.hsl.a7_zip I/MainActivity: Jni 壓縮命令 : /data/user/0/kim.hsl.a7_zip/files/7zr a /data/user/0/kim.hsl.a7_zip/files/files_jni.7z /data/user/0/kim.hsl.a7_zip/files -mx=9 -t7z 2021-05-07 13:32:19.188 31022-31022/kim.hsl.a7_zip I/octopus: jni 中處理壓縮文件命令 : /data/user/0/kim.hsl.a7_zip/files/7zr a /data/user/0/kim.hsl.a7_zip/files/files_jni.7z /data/user/0/kim.hsl.a7_zip/files -mx=9 -t7z 2021-05-07 13:32:19.188 31022-31022/kim.hsl.a7_zip I/octopus: 0 . /data/user/0/kim.hsl.a7_zip/files/7zr 2021-05-07 13:32:19.188 31022-31022/kim.hsl.a7_zip I/octopus: 1 . a 2021-05-07 13:32:19.188 31022-31022/kim.hsl.a7_zip I/octopus: 2 . /data/user/0/kim.hsl.a7_zip/files/files_jni.7z 2021-05-07 13:32:19.188 31022-31022/kim.hsl.a7_zip I/octopus: 3 . /data/user/0/kim.hsl.a7_zip/files 2021-05-07 13:32:19.188 31022-31022/kim.hsl.a7_zip I/octopus: 4 . -mx=9 2021-05-07 13:32:19.188 31022-31022/kim.hsl.a7_zip I/octopus: 5 . -t7z 2021-05-07 13:32:26.301 31022-31022/kim.hsl.a7_zip I/octopus: 7zr 命令執行完畢 !
查詢在 jni 層壓縮的文件 :
C:\Users\octop>adb shell walleye:/ $ su walleye:/ # cd /data/user/0/kim.hsl.a7_zip/files/ walleye:/data/user/0/kim.hsl.a7_zip/files # ls -la total 3028 drwxrwx--x 3 u0_a455 u0_a455 4096 2021-05-07 13:32 . drwx------ 5 u0_a455 u0_a455 4096 2021-05-07 13:19 .. -rwx------ 1 u0_a455 u0_a455 994304 2021-05-07 13:19 7zr -rw------- 1 u0_a455 u0_a455 941905 2021-05-07 13:32 files.7z -rw------- 1 u0_a455 u0_a455 1124135 2021-05-07 13:32 files_jni.7z drwx------ 3 u0_a455 u0_a455 4096 2021-05-07 13:19 unzip_file walleye:/data/user/0/kim.hsl.a7_zip/files #四、參考資料
參考資料 :
- 7-Zip 官網 : https://www.7-zip.org/
Android NDK 編譯構建腳本參考文檔 :
- ndk-build 腳本 : https://developer.android.google.cn/ndk/guides/ndk-build
- Android.mk 構建腳本 : https://developer.android.google.cn/ndk/guides/android_mk
- Application.mk 構建腳本 : https://developer.android.google.cn/ndk/guides/application_mk
博客資源 : 源碼 , 編譯后的可執行文件, 在 7zip\p7zip_16.02\CPP\ANDROID\7zr\libs\ 目錄下 ;
- 下載地址 : https://download.csdn.net/download/han1202012/18215890
- GitHub 項目源碼 : https://github.com/han1202012/7-Zip
總結
以上是生活随笔為你收集整理的【Android 安装包优化】使用 lib7zr.so 动态库处理压缩文件 ( jni 中 main 函数声明 | 命令行处理 | jni 调用 lib7zr.so 函数库处理压缩文件完整代码 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【错误记录】Android 中调用 Pr
- 下一篇: 【Android 高性能音频】Oboe