System information

  • System version: MacOS 12
  • Flutter version: 1.22.5
  • The Dart version: 2.10.4
  • Android Studio version: 4.1.1

introduce

  • APK optimizes package size

  • APK multi-channel packaging

  • APK one-click package scripts

  • IPA optimizes package size

  • IPA is packaged without a signature

  • IPA one-click package script

Script address

Github Script address

The android

APK optimizes package size

  1. Optimize the size of resources such as images and delete useless resources
  2. Choose to keep only the necessary SO libraries. The third-party SDK also tries to keep only the necessary SO library, and the optimized package body is reduced by at least tens of megabytes

See modifying the Android /app/build.gradle file

BuildTypes {release {NDK {//"armeabi","armeabi-v7a"," arm64-v8A ","x86_64","x86" //x86 is compatible with emulator abiFilters "Armeabi "," armeabi-v7A ","arm64-v8a" // There is no x86 framework, only arm32 and arm64 can be used}}}Copy the code

Android multi-channel configuration and packaging

I. Principle and introduction

  • Since Flutter V1.17, the Flutter command tool has added the ability to customize the parameters — Dart-define. We can use this command parameter to set the parameters when we pack or run the App. This allows us to capture parameters passed to Flutter code and native code, enabling multi-channel functionality.
  • Suppose we set up 5 channels 1, App Treasure, 2, Huawei store, 3, Xiaomi store, 4, OPPO store, 5, VIVO store

2. Flutter code configuration

1. Obtain parameters

Dart Configuration file path: lib/main.dart

Class EnvironmentConfig {static const CHANNEL = string.fromEnvironment ('CHANNEL'); Static const DEBUG = string.fromEnvironment ('DEBUG'); static const DEBUG = string.fromEnvironment ('DEBUG'); }Copy the code

2. Use parameters anywhere

# to obtain CHANNEL parameter value String appMarket = EnvironmentConfig. CHANNEL; String DEBUG = environmentConfig.debug;Copy the code

Three, Android code configuration

1. Obtain parameters

Configuration file path: Android /app/build.gradle

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()] } }Copy the code

2, use,

Configuration file path: Android /app/build.gradle

/ / example: Modify file name and channel parameters when packaging APK, There are also some SDK can also set the parameters in this way / / dartEnvironmentVariables CHANNEL using parameter android {android. ApplicationVariants. All {variant - > variant.outputs.all { output -> def outputFile = output.outputFile if (outputFile.name.contains("release")) { outputFileName = "APP_${getDateTime()}_${dartEnvironmentVariables.CHANNEL}.apk" } } } }Copy the code

Four, multi-channel debugging and packaging instructions

# Debug Example 1: Set channel as app treasure. Flutter run --dart-define=CHANNEL=YYB # Y flutter run --dart-define=CHANNEL=YYB --dart-define=DEBUG=Y Dart-define =CHANNEL=YYB # Y flutter build apk --dart-define=CHANNEL=YYB --dart-define=DEBUG=YCopy the code

Android one-click package script

A brief introduction

After the above configuration and optimization, we can start to execute the script packaging. This script mainly achieves the following functions

  1. Control whether to execute the flutter clean command (enter or no command within 5 seconds)
  2. Can control only a certain channel package or all channel package (Enter or 5 seconds without command input default to hit all packages)
  3. An array of channel types can be set for unlimited expansion
  4. Automatically open the folder after successful packing
  5. Realize unattended packing

Ii. Project path structure

1. The shell directory stores script files. Papk. sh is an Android script

2. Export the package file from the prod directory

3. Script content

#! /bin/sh #--------------------- must be modified: ----------------# Channels =(YYB HUAWEI MI OPPO VIVO) # Project_path =$(PWD) # Android package product folder path Prod_path = ${project_path} / prod/apk / # Flutter packaging generated initial address release_path = ${project_path} / build/app/outputs/apk/release / Clean_tips =" Execute flutter clean(default :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 "error! Only [y/n]!!" Echo $clean_tips read is_clean done tips=" 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 "error! ${c_length}!!" Echo $tips read number done # if [-d ${prod_path}]; ${prod_path} if [${is_clean} = "y"] then rm -rf ${prod_path} fi # Then the echo "= = = = = = = = = = = = = = = began to clear = = = = = = = = = = = = = = =" flutter clean fi the if (($number = = 0)); Then the echo "= = = = = = = = = = = = = = = to start building: all channel package = = = = = = = = = = = = = = =" for ((I = 0; i<${c_length}; I++)) do echo" ${channels[$I]} "flutter build apk --no-shrink --dart-define=CHANNEL=${channels[$I]} cp -r ${release_path}*.apk ${prod_path} done; Else echo "=============== is building: ${channels [$((number 1)]} channel package = = = = = = = = = = = = = = = "flutter build apk - no - the shrink --dart-define=CHANNEL=${channels[$((number-1))]} cp -r ${release_path}*. Apk ${prod_path} fi #  $prod_path)" ]; Then the echo "= = = = = = = = = = = = = = = APK package has exported: $prod_path = = = = = = = = = = = = = = =" open $prod_path else echo '= = = = = = = = = = = = = = = APK package failure is deduced ===============' exit 1 fi exit 0Copy the code

4. Script usage steps

  • 1. Create a shell folder in the project root directory
  • 2. Create the papk.sh file in the shell directory and paste the script content. Change the Channels array variable value to your own, and save the script
  • 3. In the root directory of the project, run the chmod u+x shell/papk.sh command to add the execution permission
  • 4. In the root directory of the project, run./shell/papk.sh

apple

Optimize package size

1. General optimization (distribute to App Store or play Ad Hoc test pack)

  1. It is relatively simple to optimize the size of resources such as pictures and delete useless resources, and the optimization effect is not great for general apps, so I will not elaborate on it here.
  2. Select only the necessary instruction set types to remain. Xcode 12 contains both ARMV7 and ARM64 bit instruction sets by default. We can exclude the ARMV7 instruction set when we Release the package. Just keep the ARM64 instruction set.

** If you are concerned about compatibility, below is a diagram of the corresponding phone model of the instruction set **

Second, unconventional optimization

Usually when we use Xcode distribution, Xcode will also help us optimize the code size, and the package size can be reduced by more than half. However, if we need to export an unsigned IPA package, how should we optimize the package size? Here’s my summary of how to do it

1. Derive IPA from Flutter

  1. Execute flutter build ios –release to generate Runner. App file
  2. Create a new folder Payload in the Runner. App directory and drag the app into it
  3. Right-click -> compress “Payload” to paypay.zip
  4. Rename the generated paypay. zip file to xxx.ipa to obtain the IPA installation package

2. Optimizing package size for the first time (5 steps in total)

The IPA generated above is ridiculously large. Hundreds of megabytes at every turn. So, we need to use Xcode to help optimize Runner. App. The overall step is one more step than the packaging above.

  1. Execute flutter build ios –release to generate Runner. App file
  2. ✅ open the project with Xcode and click Product -> Build. On success, a new Runner. App is generated.
  3. Create a new folder Payload in the Runner. App directory and drag the app into it
  4. Right-click -> compress “Payload” to paypay.zip
  5. Rename the generated paypay. zip file to xxx.ipa to obtain the IPA installation package

After a single operation, the inclusion can be reduced by tens or hundreds of megabytes. It looks impressive, but because the original package is so large, even with this reduction, there may still be a hundred megabytes in the end.

3. Second optimization of package size (6 steps in total)

  1. Execute flutter build ios –release to generate Runner. App file
  2. ✅ open the project with Xcode and click Product -> Build. On success, a new Runner. App is generated.
  3. ✅ Runner. App: xcrun bitcode_strip Runner.app/Frameworks/Flutter.framework/Flutter -r -o Runner.app/Frameworks/Flutter.framework/Flutter
  4. Create a new folder Payload in the Runner. App directory and drag the app into it
  5. Right-click -> compress “Payload” to paypay.zip
  6. Rename the generated paypay. zip file to xxx.ipa to obtain the IPA installation package

After another operation, the inclusion could be reduced by tens or hundreds of megabytes. Finally, it was basically optimized to less than 100 megabytes, close to the size of an IPA packet distributed with Ad Hoc

4. Some notes

1. The reason why Flutter generates Runner. App is relatively large

Ios’s Flutter binaries have added support for Bitcode, resulting in increased volume

2. How to optimize

The bitcode is removed by executing the xcrun bitcode_strip directive

The xcrun bitcode_strip directive can be searched online. I won’t go into the details of its use

Apple packs scripts with one click

A brief introduction

After passing the “general optimization” above, we can start to execute the script packaging. This script mainly implements the following functions

  1. Control whether to execute the flutter clean command (enter or no command within 5 seconds)
  2. You can control the selection of unsigned package or Ad Hoc test package (Enter or 5 seconds without command input default unsigned package)
  3. Automatically open the folder after successful packing
  4. Realize unattended packing
  5. To export unsigned packages, the script adds “unconventional optimization” related operations

Ii. Project path structure

The shell directory is used to store scripts and plist files.

2. Export the package file from the prod directory

3. Script content

#! $(PWD) #xCode builds an optimized APP file. Smaller than the Flutter build the ios Runner. The app # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- must be modified: XCODE project export path ----------------# runner_path=~/Library/Developer/Xcode/DerivedData/Runner-bsrdqyyshhsictbeoknvquvcxcsm/Build/Products/Release-iphoneos/Ru Nner. app #------------------- Optional: Own plist configuration path -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # export_plist_path = ${project_path} / shell/scriptTest plist # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - optional: Change for your APP name -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # # app_name = "APP name" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - optional: Runner to replace their own project name -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # project_name = Runner # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - optional: Replace Runner with your own sheme name --------------# scheme_name=Runner # Package mode Debug/Release development_mode=Release # Export the. Ipa file path Ipa_path =${project_PATH}/prod/ipa/ # Export signature IpA file path sign_path=${ipa_path}/sign # Export unsigned IPA file path Unsign_path =${ipa_path}/unsign # Export unsigned. Payload File path payload_path=${unsign_path}/Payload Clean_tips =" Execute flutter clean(default :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 "error! Only [y/n]!!" Echo $clean_tips read is_clean done echo "Please enter select mode (default :0) [UnSign: 0 AdHoc: 1 ] " read -t 5 number if [ ! -n "${number}" ]; Then number=0 fi while([$number!= 0]] && [[$number!= 1]]) do echo" Only 0 or 1!!" Echo "Please enter select mode? [ UnSign: 0 AdHoc: 1 ] " read number done if [ ${is_clean} = "y" ]; Then the echo "= = = = = = = = = = = = = = = began to clear = = = = = = = = = = = = = = =" flutter clean fi echo "= = = = = = = = = = = = = = = build FLUTTER_IOS engineering = = = = = = = = = = = = = = ="  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 # Delete the "product/ IPA" folder and create an empty folder if [-d ${ipa_path}]; ${ipa_path} #rm -rf ${ipa_path} if [$number == 0]; Then # without signature packaging echo "= = = = = = = = = = = = = = = are compiling the XCODE project: ${development_mode} = = = = = = = = = = = = = = =" xcodebuild build - the workspace ios/${project_name}.xcworkspace -scheme ${scheme_name} -configuration ${development_mode} mkdir -p ${payload_path} cp -r CD $${runner_path} ${payload_path} {unsign_path} echo "= = = = = = = = = = = = = = = read APP information = = = = = = = = = = = = = = =" # info. The plist path 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 "= = = = = = = = = = = = = = = optimization Framework size = = = = = = = = = = = = = = =" 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 "= = = = = = = = = = = = = = = generated IPA (compressed Payload file and modify the file named IPA) = = = = = = = = = = = = = = =" ${appName} zip -r * if [-e $unsign_path / $appName];  Then the echo "= = = = = = = = = = = = = = = IPA export package: $unsign_path / $appName = = = = = = = = = = = = = = =" open $unsign_path else echo '= = = = = = = = = = = = = = = IPA package export failure = = = = = = = = = = = = = = = 'exit 1 fi else # Ad hoc packaging echo "= = = = = = = = = = = = = = = are compiling engineering: ${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 '= = = = = = = = = = = = = = = start IPA package = = = = = = = = = = = = = = =' 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 the echo "= = = = = = = = = = = = = = = IPA export package: $sign_path / $app_name. IPA = = = = = = = = = = = = = = =" open $sign_path else echo '= = = = = = = = = = = = = = = IPA package export failure ===============' exit 1 fi fi exit 0Copy the code

4. Plist file

<? The XML version = "1.0" encoding = "utf-8"? > <! DOCTYPE plist PUBLIC "- / / / / DTD plist Apple / 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>&lt; none&gt; </string> </dict> </plist>Copy the code

5. Script usage steps

  • 1. Create a shell folder in the project root directory
  • 2. Create the papk.sh file in the shell directory and paste the script content. Change the runner_path variable to the path where your xcode exported Runner. App, then save and close
  • 3, Create the scriptTest. Plist file in the shell directory and paste the contents of the plist file. Modify the values of your Own signingCertificate and teamID, then save and close
  • 4. In the root directory of the project, run the chmod u+x shell/pipa.sh command to add the execute permission
  • 5. In the root directory of the project, run./shell/papk.sh