Android與iOS包體優(yōu)化及一鍵自動打包腳本
系統(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,我們可以用這個命令參數(shù)在打包或運(yùn)行 App 時設(shè)置參數(shù)。這樣我們就能在Flutter代碼和原生代碼中獲取傳過來的參數(shù),從而實現(xiàn)多渠道功能。
- 假設(shè)我們設(shè)置5個渠道 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ù),還有一些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í)行腳本打包了,本腳本主要實現(xiàn)了以下功能
- 可控制是否執(zhí)行 flutter clean 清理指令(回車或者5秒無指令輸入默認(rèn)不清理)
- 可控制只打某個渠道包或者全部渠道包(回車或者5秒無指令輸入默認(rèn)打全部包)
- 可設(shè)置渠道種類數(shù)組,可無限擴(kuò)展
- 成功打包后自動打開文件夾
- 實現(xiàn)無人值守打包
二、項目路徑結(jié)構(gòu)
1、shell 目錄存放腳本文件 ,papk.sh 是安卓腳本
2、prod 目錄導(dǎo)出打包文件

三、腳本內(nèi)容
#!/bin/sh
#---------------------必須修改:渠道數(shù)組----------------#
channels=(YYB HUAWEI MI OPPO VIVO)
#當(dāng)前工程絕對路徑
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 "錯誤!只能輸入[ y/n ] ?。?!"
echo $clean_tips
read is_clean
done
tips="請輸入選擇渠道(默認(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 "錯誤!只能輸入0到${c_length} ?。?!"
echo $tips
read number
done
#如果有product/apk文件夾則刪除,然后再創(chuàng)建一個空文件夾
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、在項目根目錄創(chuàng)建一個shell文件夾
- 2、在shell目錄創(chuàng)建papk.sh文件,并粘貼腳本內(nèi)容。修改channels渠道數(shù)組變量值為自己的,然后保存腳本
- 3、項目根目錄執(zhí)行命令添加執(zhí)行權(quán)限: chmod u+x shell/papk.sh
- 4、項目根目錄執(zhí)行命令:./shell/papk.sh
蘋果優(yōu)化包體大小
一、常規(guī)優(yōu)化(分發(fā)到 App Store 或者 打Ad hoc 測試包)
- 優(yōu)化圖片等資源大小,刪除無用資源,比較簡單,而且對于一般的app來說優(yōu)化效果不大,這里就不詳細(xì)說明了。
- 只選擇保留必要的指令集類型。xcode 12以上默認(rèn)是包含armv7和arm64位兩種指令集。我們打Release包時可以排除armv7指令集。只保留arm64指令集就可以了 看圖操作:

** 如果擔(dān)心兼容性的,下面再附一張指令集對應(yīng)手機(jī)型號的圖 **

二、非常規(guī)優(yōu)化
一般情況下我們使用xcode分發(fā)的時候,xcode還會幫我們優(yōu)化一次代碼大小的,包體大概能減少一半以上。然鵝,如果需要自己導(dǎo)出一個無簽名的IPA包時我們應(yīng)該怎么優(yōu)化包體大?。肯旅媸俏铱偨Y(jié)的操作步驟
1、Flutter導(dǎo)出IPA(共4步)
- 執(zhí)行flutter build ios --release 生成Runner.app文件
- 在Runner.app目錄下新建一個Payload文件夾, 并將該 app 拖進(jìn)去
- 右鍵->壓縮"Payload"為Payload.zip
- 將生成的 Payload.zip 文件更名為 xxx.ipa 即可得到 ipa 安裝包
2、第一次優(yōu)化包體大?。ü?步)
上面生成的ipa大的離譜。動不動就幾百兆。所以,我們需要利用Xcode來幫忙優(yōu)化一下Runner.app??傮w步驟比上面的打包多了一步。
- 執(zhí)行flutter build ios --release 生成Runner.app文件
- ? 使用xcode打開項目,然后點(diǎn)擊product->build。成功后生成一個新的Runner.app。
- 在Runner.app目錄下新建一個Payload文件夾, 并將該 app 拖進(jìn)去
- 右鍵->壓縮"Payload"為Payload.zip
- 將生成的 Payload.zip 文件更名為 xxx.ipa 即可得到 ipa 安裝包
一頓操作后,包體大概能減少幾十或者上百兆??粗芸捎^,但是由于原來的包實在太大,即使減了這么多,可能最終還有一百來兆。
3、第二次優(yōu)化包體大?。ü?步)
- 執(zhí)行flutter build ios --release 生成Runner.app文件
- ? 使用xcode打開項目,然后點(diǎn)擊product->build。成功后生成一個新的Runner.app。
- ? Runner.app目錄下執(zhí)行指令: xcrun bitcode_strip Runner.app/Frameworks/Flutter.framework/Flutter -r -o Runner.app/Frameworks/Flutter.framework/Flutter
- 在Runner.app目錄下新建一個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)制文件增加了對bitcode的支持,從而導(dǎo)致體積增大
2、 如何優(yōu)化
執(zhí)行 xcrun bitcode_strip 指令就能去掉bitcode
xcrun bitcode_strip 指令大家可以自行網(wǎng)上搜索。詳細(xì)的使用我就不細(xì)說了
蘋果一鍵打包腳本
一、簡單介紹
通過上面的”常規(guī)優(yōu)化“后我們就能開始執(zhí)行腳本打包了,本腳本主要實現(xiàn)了以下功能
- 可控制是否執(zhí)行 flutter clean 清理指令(回車或者5秒無指令輸入默認(rèn)不清理)
- 可控制選擇打無簽名包還是Ad hoc測試包(回車或者5秒無指令輸入默認(rèn)無簽名包)
- 成功打包后自動打開文件夾
- 實現(xiàn)無人值守打包
- 如果要導(dǎo)出無簽名包,腳本中添加了”非常規(guī)的優(yōu)化“相關(guān)的操作
二、項目路徑結(jié)構(gòu)
1、shell 目錄存放腳本和plist文件, pipa.sh 是蘋果腳本,
2、prod 目錄導(dǎo)出打包文件

三、腳本內(nèi)容
#!/bin/sh
#當(dāng)前工程絕對路徑
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 "錯誤!只能輸入[ y/n ] ?。?!"
echo $clean_tips
read is_clean
done
echo "請輸入選擇模式(默認(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 "錯誤!只能輸入0或者1!?。?
echo "請輸入選擇模式? [ 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)建一個空文件夾
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、在項目根目錄創(chuàng)建一個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、項目根目錄執(zhí)行命令添加執(zhí)行權(quán)限: chmod u+x shell/pipa.sh
- 5、項目根目錄執(zhí)行命令:./shell/papk.sh
以上就是安卓與iOS包體優(yōu)化及一鍵自動打包腳本的詳細(xì)內(nèi)容,更多關(guān)于安卓 iOS打包腳本的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實戰(zhàn)打飛機(jī)游戲之怪物(敵機(jī))類的實現(xiàn)(4)
這篇文章主要為大家詳細(xì)介紹了Android實戰(zhàn)打飛機(jī)游戲之怪物(敵機(jī))類的實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-07-07
Android自定義view實現(xiàn)動態(tài)柱狀圖
這篇文章主要為大家詳細(xì)介紹了Android自定義view實現(xiàn)動態(tài)柱狀圖的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
Flutter學(xué)習(xí)教程之Route跳轉(zhuǎn)以及數(shù)據(jù)傳遞
這篇文章主要給大家介紹了關(guān)于Flutter學(xué)習(xí)教程之Route跳轉(zhuǎn)以及數(shù)據(jù)傳遞的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Android 和 windows C/C++/QT通訊時字節(jié)存儲
本篇文章主要介紹 Android和Windows 通訊時數(shù)據(jù)地址的理解,這里提供代碼實例進(jìn)行分析,有需要參考的朋友可以看下2016-07-07
Android持久化技術(shù)之SharedPreferences存儲實例詳解
這篇文章主要介紹了Android持久化技術(shù)之SharedPreferences存儲,結(jié)合實例形式較為詳細(xì)的分析了SharedPreferences存儲的原理、應(yīng)用及具體實現(xiàn)方法,需要的朋友可以參考下2016-01-01
android studio編譯jar包或者aar包的方法教程詳解
這篇文章主要介紹了android studio編譯jar包或者aar包的方法教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03
Android中創(chuàng)建快捷方式及刪除快捷方式實現(xiàn)方法
這篇文章主要介紹了Android中創(chuàng)建快捷方式及刪除快捷方式實現(xiàn)方法,本文直接給出實現(xiàn)代碼,需要的朋友可以參考下2015-06-06
Android 模仿QQ側(cè)滑刪除ListView功能示例
這篇文章主要介紹了Android 模仿QQ側(cè)滑刪除ListView功能示例,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-03-03

