Android與iOS包體優(yōu)化及一鍵自動(dòng)打包腳本
系統(tǒng)信息
- System version: MacOS 12
- Flutter version: 1.22.5
- Dart version: 2.10.4
- Android Studio version: 4.1.1
介紹
- APK優(yōu)化包體大小
- APK多渠道打包
- APK一鍵打包腳本
- IPA優(yōu)化包體大小
- IPA無簽名打包
- IPA一鍵打包腳本
腳本地址
安卓APK優(yōu)化包體大小
- 優(yōu)化圖片等資源大小,刪除無用資源
- 只選擇保留必要的so庫。第三方SDK也盡量只里保留必要的so庫,優(yōu)化后的包體至少減少幾十兆
參考修改android/app/build.gradle 文件
buildTypes { release { ndk{ //"armeabi","armeabi-v7a","arm64-v8a","x86_64","x86" //x86是兼容模擬器的 abiFilters "armeabi","armeabi-v7a","arm64-v8a" //手機(jī)沒什么x86框架的,只包含arm32和arm64即可 } } }
安卓多渠道配置與打包
一、原理與介紹
- 從 Flutter v1.17 開始,F(xiàn)lutter 命令工具增加了自定義參數(shù)的功能 --dart-define,我們可以用這個(gè)命令參數(shù)在打包或運(yùn)行 App 時(shí)設(shè)置參數(shù)。這樣我們就能在Flutter代碼和原生代碼中獲取傳過來的參數(shù),從而實(shí)現(xiàn)多渠道功能。
- 假設(shè)我們?cè)O(shè)置5個(gè)渠道 1、應(yīng)用寶, 2、華為商店, 3、小米商店,4、OPPO商店 5、VIVO商店
二、Flutter代碼配置
1、獲取參數(shù)
配置文件路徑:lib/main.dart
/// 這里定義環(huán)境變量配置 class EnvironmentConfig { static const CHANNEL = String.fromEnvironment('CHANNEL'); //DEBUG = Y 是調(diào)試模式,其他為生產(chǎn)模式 static const DEBUG = String.fromEnvironment('DEBUG'); }
2、任意的地方使用參數(shù)
#獲取CHANNEL 參數(shù)值 String appMarket = EnvironmentConfig.CHANNEL; #獲取DEBUG 參數(shù)值 String debug = EnvironmentConfig.DEBUG;
三、Android代碼配置
1、獲取參數(shù)
配置文件路徑:android/app/build.gradle
/// 獲取渠道參數(shù)使用,這里設(shè)置一下默認(rèn)值 def dartEnvironmentVariables = [ CHANNEL: 'YYB', DEBUG: '', ] if (project.hasProperty('dart-defines')) { dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines') .split(',') .collectEntries { entry -> def pair = URLDecoder.decode(entry).split('=') [(pair.first()): pair.last()] } }
2、使用
配置文件路徑:android/app/build.gradle
//例子:打包APK時(shí)修改文件名帶上渠道參數(shù),還有一些SDK也可以通過這種方式設(shè)置參數(shù) //dartEnvironmentVariables.CHANNEL 使用參數(shù) android{ android.applicationVariants.all { variant -> variant.outputs.all { output -> def outputFile = output.outputFile if (outputFile.name.contains("release")) { outputFileName = "APP_${getDateTime()}_${dartEnvironmentVariables.CHANNEL}.apk" } } } }
四、多渠道調(diào)試與打包指令
# 調(diào)試?yán)?:設(shè)置渠道為應(yīng)用寶。 flutter run --dart-define=CHANNEL=YYB # 調(diào)試?yán)?:設(shè)置渠道為應(yīng)用寶。DEBUG參數(shù)是Y flutter run --dart-define=CHANNEL=YYB --dart-define=DEBUG=Y #打包例子1:打包應(yīng)用寶渠道包 flutter build apk --dart-define=CHANNEL=YYB #打包例子2:打包應(yīng)用寶渠道包,DEBUG參數(shù)是Y flutter build apk --dart-define=CHANNEL=YYB --dart-define=DEBUG=Y
安卓一鍵打包腳本
一、簡單介紹
通過上面的配置和優(yōu)化后我們就能開始執(zhí)行腳本打包了,本腳本主要實(shí)現(xiàn)了以下功能
- 可控制是否執(zhí)行 flutter clean 清理指令(回車或者5秒無指令輸入默認(rèn)不清理)
- 可控制只打某個(gè)渠道包或者全部渠道包(回車或者5秒無指令輸入默認(rèn)打全部包)
- 可設(shè)置渠道種類數(shù)組,可無限擴(kuò)展
- 成功打包后自動(dòng)打開文件夾
- 實(shí)現(xiàn)無人值守打包
二、項(xiàng)目路徑結(jié)構(gòu)
1、shell 目錄存放腳本文件 ,papk.sh 是安卓腳本
2、prod 目錄導(dǎo)出打包文件
三、腳本內(nèi)容
#!/bin/sh #---------------------必須修改:渠道數(shù)組----------------# channels=(YYB HUAWEI MI OPPO VIVO) #當(dāng)前工程絕對(duì)路徑 project_path=$(pwd) #安卓包product文件夾路徑 prod_path=${project_path}/prod/apk/ #Flutter打包生成的最初地址 release_path=${project_path}/build/app/outputs/apk/release/ clean_tips="執(zhí)行flutter clean(默認(rèn):n) [ y/n ]" echo $clean_tips read -t 5 is_clean if [ ! -n "${is_clean}" ];then is_clean="n" fi while([[ $is_clean != "y" ]] && [[ $is_clean != "n" ]]) do echo "錯(cuò)誤!只能輸入[ y/n ] !?。? echo $clean_tips read is_clean done tips="請(qǐng)輸入選擇渠道(默認(rèn):0) [ ALL: 0 " c_length=${#channels[@]}; for(( i=0; i<$c_length; i++)) do if (($i < $c_length-1 )); then tips="${tips}${channels[i]}: $((i+1)) " else tips="${tips}${channels[i]}: $((i+1)) ]" fi done; echo $tips read -t 5 number if [ ! -n "${number}" ];then number=0 fi while(( $number < "0" || $number > $c_length )) do echo "錯(cuò)誤!只能輸入0到${c_length} ?。。? echo $tips read number done #如果有product/apk文件夾則刪除,然后再創(chuàng)建一個(gè)空文件夾 if [ -d ${prod_path} ]; then rm -rf ${prod_path} fi #創(chuàng)建目錄 mkdir -p ${prod_path} if [ ${is_clean} = "y" ];then echo "=============== 開始清理 ===============" flutter clean fi if (($number == 0 )); then echo "=============== 開始構(gòu)建:全部渠道包 ===============" for(( i=0;i<${c_length};i++)) do echo "正在構(gòu)建:${channels[$i]} 渠道包" flutter build apk --no-shrink --dart-define=CHANNEL=${channels[$i]} cp -R ${release_path}*.apk ${prod_path} done; else echo "=============== 正在構(gòu)建:${channels[$((number-1))]} 渠道包 ===============" flutter build apk --no-shrink --dart-define=CHANNEL=${channels[$((number-1))]} cp -R ${release_path}*.apk ${prod_path} fi #判斷apk目錄下是否有文件 if [ "$(ls -A $prod_path)" ]; then echo "=============== APK包已導(dǎo)出:$prod_path ===============" open $prod_path else echo '=============== APK包導(dǎo)出失敗 ===============' exit 1 fi exit 0
四、腳本使用步驟
- 1、在項(xiàng)目根目錄創(chuàng)建一個(gè)shell文件夾
- 2、在shell目錄創(chuàng)建papk.sh文件,并粘貼腳本內(nèi)容。修改channels渠道數(shù)組變量值為自己的,然后保存腳本
- 3、項(xiàng)目根目錄執(zhí)行命令添加執(zhí)行權(quán)限: chmod u+x shell/papk.sh
- 4、項(xiàng)目根目錄執(zhí)行命令:./shell/papk.sh
蘋果優(yōu)化包體大小
一、常規(guī)優(yōu)化(分發(fā)到 App Store 或者 打Ad hoc 測(cè)試包)
- 優(yōu)化圖片等資源大小,刪除無用資源,比較簡單,而且對(duì)于一般的app來說優(yōu)化效果不大,這里就不詳細(xì)說明了。
- 只選擇保留必要的指令集類型。xcode 12以上默認(rèn)是包含armv7和arm64位兩種指令集。我們打Release包時(shí)可以排除armv7指令集。只保留arm64指令集就可以了 看圖操作:
** 如果擔(dān)心兼容性的,下面再附一張指令集對(duì)應(yīng)手機(jī)型號(hào)的圖 **
二、非常規(guī)優(yōu)化
一般情況下我們使用xcode分發(fā)的時(shí)候,xcode還會(huì)幫我們優(yōu)化一次代碼大小的,包體大概能減少一半以上。然鵝,如果需要自己導(dǎo)出一個(gè)無簽名的IPA包時(shí)我們應(yīng)該怎么優(yōu)化包體大???下面是我總結(jié)的操作步驟
1、Flutter導(dǎo)出IPA(共4步)
- 執(zhí)行flutter build ios --release 生成Runner.app文件
- 在Runner.app目錄下新建一個(gè)Payload文件夾, 并將該 app 拖進(jìn)去
- 右鍵->壓縮"Payload"為Payload.zip
- 將生成的 Payload.zip 文件更名為 xxx.ipa 即可得到 ipa 安裝包
2、第一次優(yōu)化包體大?。ü?步)
上面生成的ipa大的離譜。動(dòng)不動(dòng)就幾百兆。所以,我們需要利用Xcode來幫忙優(yōu)化一下Runner.app??傮w步驟比上面的打包多了一步。
- 執(zhí)行flutter build ios --release 生成Runner.app文件
- ? 使用xcode打開項(xiàng)目,然后點(diǎn)擊product->build。成功后生成一個(gè)新的Runner.app。
- 在Runner.app目錄下新建一個(gè)Payload文件夾, 并將該 app 拖進(jìn)去
- 右鍵->壓縮"Payload"為Payload.zip
- 將生成的 Payload.zip 文件更名為 xxx.ipa 即可得到 ipa 安裝包
一頓操作后,包體大概能減少幾十或者上百兆。看著很可觀,但是由于原來的包實(shí)在太大,即使減了這么多,可能最終還有一百來兆。
3、第二次優(yōu)化包體大?。ü?步)
- 執(zhí)行flutter build ios --release 生成Runner.app文件
- ? 使用xcode打開項(xiàng)目,然后點(diǎn)擊product->build。成功后生成一個(gè)新的Runner.app。
- ? Runner.app目錄下執(zhí)行指令: xcrun bitcode_strip Runner.app/Frameworks/Flutter.framework/Flutter -r -o Runner.app/Frameworks/Flutter.framework/Flutter
- 在Runner.app目錄下新建一個(gè)Payload文件夾, 并將該 app 拖進(jìn)去
- 右鍵->壓縮"Payload"為Payload.zip
- 將生成的 Payload.zip 文件更名為 xxx.ipa 即可得到 ipa 安裝包
再一頓操作下來后,包體大概又能減少幾十或者上百兆。終于基本優(yōu)化到一百兆以內(nèi),接近用Ad hoc分發(fā)的ipa包的大小
4、一些說明
1、 Flutter生成Runner.app比較大的原因
ios的Flutter二進(jìn)制文件增加了對(duì)bitcode的支持,從而導(dǎo)致體積增大
2、 如何優(yōu)化
執(zhí)行 xcrun bitcode_strip 指令就能去掉bitcode
xcrun bitcode_strip 指令大家可以自行網(wǎng)上搜索。詳細(xì)的使用我就不細(xì)說了
蘋果一鍵打包腳本
一、簡單介紹
通過上面的”常規(guī)優(yōu)化“后我們就能開始執(zhí)行腳本打包了,本腳本主要實(shí)現(xiàn)了以下功能
- 可控制是否執(zhí)行 flutter clean 清理指令(回車或者5秒無指令輸入默認(rèn)不清理)
- 可控制選擇打無簽名包還是Ad hoc測(cè)試包(回車或者5秒無指令輸入默認(rèn)無簽名包)
- 成功打包后自動(dòng)打開文件夾
- 實(shí)現(xiàn)無人值守打包
- 如果要導(dǎo)出無簽名包,腳本中添加了”非常規(guī)的優(yōu)化“相關(guān)的操作
二、項(xiàng)目路徑結(jié)構(gòu)
1、shell 目錄存放腳本和plist文件, pipa.sh 是蘋果腳本,
2、prod 目錄導(dǎo)出打包文件
三、腳本內(nèi)容
#!/bin/sh #當(dāng)前工程絕對(duì)路徑 project_path=$(pwd) #xCode build 出來的APP文件有所優(yōu)化,比Flutter build ios 的Runner.app要小 #------------------必須修改:XCODE工程導(dǎo)出路徑----------------# runner_path=~/Library/Developer/Xcode/DerivedData/Runner-bsrdqyyshhsictbeoknvquvcxcsm/Build/Products/Release-iphoneos/Runner.app #-------------------可選:自己的plist配置路徑------------------# export_plist_path=${project_path}/shell/scriptTest.plist #-------------------可選:修改為自己的APP名稱------------------# app_name="APP名稱" #----------------可選:將Runner替換成自己的工程名---------------# project_name=Runner #----------------可選:將Runner替換成自己的sheme名--------------# scheme_name=Runner #打包模式 Debug/Release development_mode=Release #導(dǎo)出.ipa文件所在路徑 ipa_path=${project_path}/prod/ipa/ #導(dǎo)出簽名.ipa文件所在路徑 sign_path=${ipa_path}/sign #導(dǎo)出未簽名.ipa文件所在路徑 unsign_path=${ipa_path}/unsign #導(dǎo)出未簽名.Payload文件所在路徑 payload_path=${unsign_path}/Payload clean_tips="執(zhí)行flutter clean(默認(rèn):n) [ y/n ]" echo $clean_tips read -t 5 is_clean if [ ! -n "${is_clean}" ];then is_clean="n" fi while([[ $is_clean != "y" ]] && [[ $is_clean != "n" ]]) do echo "錯(cuò)誤!只能輸入[ y/n ] !?。? echo $clean_tips read is_clean done echo "請(qǐng)輸入選擇模式(默認(rèn):0) [ UnSign: 0 AdHoc: 1 ] " read -t 5 number if [ ! -n "${number}" ];then number=0 fi while([[ $number != 0 ]] && [[ $number != 1 ]]) do echo "錯(cuò)誤!只能輸入0或者1?。?!" echo "請(qǐng)輸入選擇模式? [ UnSign: 0 AdHoc: 1 ] " read number done if [ ${is_clean} = "y" ];then echo "=============== 開始清理 ===============" flutter clean fi echo "=============== 構(gòu)建FLUTTER_IOS工程 ===============" if [ $number == 0 ];then flutter build ios --release --no-codesign else flutter build ios fi #flutter build ios --release --no-codesign --obfuscate --split-debug-info=./symbols #如果有product/ipa文件夾則刪除,然后再創(chuàng)建一個(gè)空文件夾 if [ -d ${ipa_path} ]; then rm -rf ${ipa_path} fi #創(chuàng)建目錄 mkdir -p ${ipa_path} #rm -rf ${ipa_path} if [ $number == 0 ];then #無簽名打包 echo "=============== 正在編譯XCODE工程:${development_mode} ===============" xcodebuild build -workspace ios/${project_name}.xcworkspace -scheme ${scheme_name} -configuration ${development_mode} mkdir -p ${payload_path} cp -r ${runner_path} ${payload_path} cd ${unsign_path} echo "=============== 讀取APP信息 ===============" #info.plist路徑 info_plist="Payload/Runner.app/info.plist" version=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$info_plist") build=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$info_plist") time=$(date "+%Y%m%d_%H%M") appName="$app_name""_v$version""_b$build""_$time.ipa" echo "=============== 優(yōu)化Framework大小 ===============" xcrun bitcode_strip ${payload_path}/Runner.app/Frameworks/Flutter.framework/Flutter -r -o ${payload_path}/Runner.app/Frameworks/Flutter.framework/Flutter xcrun bitcode_strip ${payload_path}/Runner.app/Frameworks/AgoraRtcKit.framework/AgoraRtcKit -r -o ${payload_path}/Runner.app/Frameworks/AgoraRtcKit.framework/AgoraRtcKit xcrun bitcode_strip ${payload_path}/Runner.app/Frameworks/App.framework/App -r -o ${payload_path}/Runner.app/Frameworks/App.framework/App echo "=============== 生成IPA(壓縮Payload文件并修改文件名為IPA) ===============" zip -r ${appName} * if [ -e $unsign_path/$appName ]; then echo "=============== IPA包已導(dǎo)出:$unsign_path/$appName ===============" open $unsign_path else echo '=============== IPA包導(dǎo)出失敗 ===============' exit 1 fi else #Ad hoc 打包 echo "=============== 正在編譯工程:${development_mode} ===============" xcodebuild \ archive -workspace ${project_path}/ios/${project_name}.xcworkspace \ -scheme ${scheme_name} \ -configuration ${development_mode} \ -archivePath ${ipa_path}/${project_name}.xcarchive -quiet || exit echo '' echo '=============== 開始IPA打包 ===============' xcodebuild -exportArchive -archivePath ${ipa_path}/${project_name}.xcarchive \ -configuration ${development_mode} \ -exportPath ${sign_path} \ -exportOptionsPlist ${export_plist_path} \ -quiet || exit if [ -e $sign_path/$app_name.ipa ]; then echo "=============== IPA包已導(dǎo)出:$sign_path/$app_name.ipa ===============" open $sign_path else echo '=============== IPA包導(dǎo)出失敗 ===============' exit 1 fi fi exit 0
四、plist文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>compileBitcode</key> <true/> <key>destination</key> <string>export</string> <key>method</key> <string>ad-hoc</string> <key>signingCertificate</key> <string>XXXXXXXX</string> <key>signingStyle</key> <string>automatic</string> <key>stripSwiftSymbols</key> <true/> <key>teamID</key> <string>XXXXXX</string> <key>thinning</key> <string><none></string> </dict> </plist>
五、腳本使用步驟
- 1、在項(xiàng)目根目錄創(chuàng)建一個(gè)shell文件夾
- 2、在shell目錄創(chuàng)建papk.sh文件,并粘貼腳本內(nèi)容。修改runner_path變量值為自己xcode導(dǎo)出Runner.app的路徑,然后保存并關(guān)閉
- 3、在shell目錄創(chuàng)建scriptTest.plist文件,并粘貼plist文件內(nèi)容。修改自己的signingCertificate和teamID的值,然后保存并關(guān)閉
- 4、項(xiàng)目根目錄執(zhí)行命令添加執(zhí)行權(quán)限: chmod u+x shell/pipa.sh
- 5、項(xiàng)目根目錄執(zhí)行命令:./shell/papk.sh
以上就是安卓與iOS包體優(yōu)化及一鍵自動(dòng)打包腳本的詳細(xì)內(nèi)容,更多關(guān)于安卓 iOS打包腳本的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實(shí)戰(zhàn)打飛機(jī)游戲之怪物(敵機(jī))類的實(shí)現(xiàn)(4)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)戰(zhàn)打飛機(jī)游戲之怪物(敵機(jī))類的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07Android自定義view實(shí)現(xiàn)動(dòng)態(tài)柱狀圖
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)動(dòng)態(tài)柱狀圖的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Flutter學(xué)習(xí)教程之Route跳轉(zhuǎn)以及數(shù)據(jù)傳遞
這篇文章主要給大家介紹了關(guān)于Flutter學(xué)習(xí)教程之Route跳轉(zhuǎn)以及數(shù)據(jù)傳遞的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Android 和 windows C/C++/QT通訊時(shí)字節(jié)存儲(chǔ)
本篇文章主要介紹 Android和Windows 通訊時(shí)數(shù)據(jù)地址的理解,這里提供代碼實(shí)例進(jìn)行分析,有需要參考的朋友可以看下2016-07-07Android持久化技術(shù)之SharedPreferences存儲(chǔ)實(shí)例詳解
這篇文章主要介紹了Android持久化技術(shù)之SharedPreferences存儲(chǔ),結(jié)合實(shí)例形式較為詳細(xì)的分析了SharedPreferences存儲(chǔ)的原理、應(yīng)用及具體實(shí)現(xiàn)方法,需要的朋友可以參考下2016-01-01android studio編譯jar包或者aar包的方法教程詳解
這篇文章主要介紹了android studio編譯jar包或者aar包的方法教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03Android中創(chuàng)建快捷方式及刪除快捷方式實(shí)現(xiàn)方法
這篇文章主要介紹了Android中創(chuàng)建快捷方式及刪除快捷方式實(shí)現(xiàn)方法,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-06-06Android 模仿QQ側(cè)滑刪除ListView功能示例
這篇文章主要介紹了Android 模仿QQ側(cè)滑刪除ListView功能示例,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03