Flutter 多渠道打包實踐

Cheney2006發表於2020-03-31

背景

  在原生開發中經常用到根據不同的渠道打不同的包,主要有以下場景:

  • 開發環境、測試環境、生產環境等

  不同的環境對應的要求也一樣,這樣根據需要打出不同的包更便於開發、測試同學使用。

  • 各個應用市場等

  由於國記憶體在著有眾多的應用市場,在不同的應用市場可能有不同的統計需求,為此開發人員需要為每個應用市場釋出一個安裝包,在安裝包中新增不同的標識,以此區分各個渠道,方便統計app在市場的各種效果。

  因此,每當發新版本時,運營會提供一個渠道列表,開發同學會根據這些渠道相應地生成等量的渠道包。隨著渠道越來越多,為了提高渠道打包的效率,因此催生了對多渠道打包的方式的研究。

  同樣在 Flutter的專案開發中也涉及到這個問題,在這裡主要講怎麼配置開發包與生產包,並快速打出或執行相應的包。

  大這裡我將分別從Android 端、IOS 端、Flutter 端進行講解配置。

Android 配置

對於android,我們只要在app gradle模組中配置productFlavors即可,這裡我們在build.gradle 中 android 下面定義了dev、production兩種flavors。


def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
//apply from: "kappaBuild.gradle"

def rootPath=buildscript.sourceFile.parentFile





android {
   
    compileSdkVersion 28



    defaultConfig {
        applicationId "com.nongfadai.kappa"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

  
        resValue "string", "app_name", "多渠道打包"   // 設定預設的app_name

        manifestPlaceholders = [
                QQ_APP_ID: "1233",
            CHANNEL_NAME: "app",
            LOCATION_APP_KEY : "xxx", /// 高德地圖key
        ]
    }


    buildTypes {

        debug{
            signingConfig signingConfigs.debug
        }


        release {
            // Signing with the debug keys for now, so `flutter run --release` works.
            buildConfigField "boolean", "LOG_DEBUG", "false"
            signingConfig signingConfigs.release
              //程式碼混淆
            minifyEnabled true
            //程式碼壓縮
            useProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//            //移除無用的resource檔案
            shrinkResources true
            multiDexEnabled = true
            ndk {
                abiFilters "armeabi-v7a"
            }

        }

    }

    flavorDimensions "app"

    productFlavors {
        dev {//development
            dimension "app"
            resValue "string", "app_name", "多渠道打包${defaultConfig.versionCode}"   // 設定預設的app_name
            applicationId "${defaultConfig.applicationId}.dev"
            manifestPlaceholders = [
                    QQ_APP_ID: "xxx",
                    CHANNEL_NAME: "dev",
                    LOCATION_APP_KEY : "xxx", /// 高德地圖key
            ]
        }


        production{
            dimension "app"
        }
    }



}

flutter {
    source '../..'
}

dependencies {
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
}

複製程式碼

配置非常簡單,這裡還可以設定不同的應用程式applicationId字尾, 還設定了不同flavors 的app_name,CHANNEL_NAME。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xx.xx">

    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->


    
    <uses-permission android:name="android.permission.INTERNET" />



    <application
        android:usesCleartextTraffic="true"
        android:name="io.flutter.app.FlutterApplication"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- This keeps the window background of the activity showing
                 until Flutter renders its first frame. It can be removed if
                 there is no splash screen (such as the default splash screen
                 defined in @style/LaunchTheme). -->
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>


        <!--        多渠道打包           -->

        <meta-data
            android:name="UMENG_CHANNEL"
            android:value="${CHANNEL_NAME}" />


    </application>
</manifest>

複製程式碼

IOS 配置

對於 ios,我們只要在ios / Flutter資料夾中為每一個flavor建立相應的配置檔案,就像Flutter中預設預定義Debug.xcconfig和Release.xcconfig一樣,因為production正式包不用變包名與應用名,所以這裡就只建立 dev_debug.xcconfig、dev_release.xcconfig兩個檔案,

Flutter 多渠道打包實踐

並在dev flavor對應的xcconfig中配置bundle_suffix,name_suffix。

#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
bundle_suffix=.dev
name_suffix=${FLUTTER_BUILD_NUMBER}

複製程式碼

並在Info.plist檔案新增您定義的變數

Flutter 多渠道打包實踐

然後針對每個 flavor 建立對應的scheme

Flutter 多渠道打包實踐
記得在對話方塊中勾選上 shared

Flutter 多渠道打包實踐

現在選擇Runner專案並新增您需要選擇的配置作為之前建立的配置檔案作為配置檔案。

Flutter 多渠道打包實踐

注意,對於每個以Release- [flavorName]和Debug- [flavorName]命名的falvor都有兩個配置。這裡注意名字不要重複。

為了使在打包、釋出時iOS應用使用正確的配置,這裡需要編輯scheme,將構建配置設定為所需的配置:

Flutter 多渠道打包實踐

Flutter 配置

將main.dart重新命名為main_common.dart,把公共配置、執行部分定義在這裡,然後建立 main_dev.dart和main_product.dart檔案,引入main_common,根據需要在main_dev.dart和main_product.dart檔案設定不同的配置引數。

可在 main_dev.dart 中設定網路代理、以及第三方庫等的測試key。

import 'dart:io';

import 'package:dio/adapter.dart';
import 'package:flutter_common_utils/http/http_manager.dart';

import 'main_common.dart';

/// @desc 開發、測試環境入口
/// @time 2019-07-17 15:08
/// @author Cheney
Future<Null> main() async {
  await initConfig();

  //debug 抓包
  (HttpManager().client.httpClientAdapter as DefaultHttpClientAdapter)
      .onHttpClientCreate = (client) {
    client.findProxy = (uri) {
      return "PROXY http://10.1.10.111:8080";
    };
    client.badCertificateCallback =
        (X509Certificate cert, String host, int port) {
      return true;
    };
  };

  ///第三方庫 測試 key
}

複製程式碼

在main_product.dar配置正式的第三方庫 key等。

import 'main_common.dart';

/// @desc 正式環境入口
/// @time 2019-07-17 15:08
/// @author Cheney
Future<Null> main() async {
  await initConfig();

  ///第三方庫 正式 key todo

  initMaterialApp();
}

複製程式碼

在main_common 中初始公共配置:

import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_common_utils/http/http_manager.dart';
import 'package:flutter_common_utils/log_util.dart';
import 'package:flutter_common_utils/sp_util.dart';
import 'package:oktoast/oktoast.dart';

import 'main.dart';

/// @desc 入口公共部分
/// @time 2019-07-17 15:08
/// @author Cheney
Future<Null> initConfig() async {
  await SpUtil().init();

  //日誌輸出
  LogUtil.init(isDebug: true);

  //初始化儲存管理
//  await StorageUtil.getInstance();
}

///初始化 App
void initMaterialApp() {
  HttpManager().init(
    baseUrl: "xxx",
    interceptors: [],
  );

  runApp(OKToast(child: HomeApp()));
}

複製程式碼

這樣在命令列就可以分渠道執行了:

flutter build --flavor dev -t lib/main-dev.dart

flutter build --flavor production -t lib/main-production.dart

如果你想直接大 IDE中直接分渠道包執行,則要編輯配置建立兩個對應的執行配置:

Flutter 多渠道打包實踐

Flutter 多渠道打包實踐
這裡建立了兩個 flutter 執行配置項,main_dev、main_production。

在Dart entrypoint 中選擇上面建立的對應的 main_dev.dart、dart_production.dart。

在Build flavor 中選擇對應的 flavor(dev、production)。

此時在執行對應的main_dev、main_production就可以了。

最後

  如果在使用過程遇到問題,歡迎下方留言交流。

參考資料:

學習資料

請大家不吝點贊!因為您的點贊是對我最大的鼓勵,謝謝!

相關文章