背景
??在原生開(kāi)發(fā)中經(jīng)常要根據(jù)不同的渠道打不同的包,主要有以下場(chǎng)景:
- 開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境、生產(chǎn)環(huán)境等
??不同的環(huán)境對(duì)應(yīng)的要求也不一樣,這樣就要求根據(jù)需求打出不同的包以便于開(kāi)發(fā)、測(cè)試方便的使用。
- 各個(gè)應(yīng)用市場(chǎng)等
??由于國(guó)內(nèi)存在著有眾多的應(yīng)用市場(chǎng),在不同的應(yīng)用市場(chǎng)可能有不同的統(tǒng)計(jì)需求,為此開(kāi)發(fā)人員需要為每個(gè)應(yīng)用市場(chǎng)發(fā)布一個(gè)安裝包,在安裝包中添加不同的標(biāo)識(shí),以此區(qū)分各個(gè)渠道,方便統(tǒng)計(jì)app在應(yīng)用市場(chǎng)的各種效果。
??因此,每當(dāng)發(fā)新版本時(shí),運(yùn)營(yíng)會(huì)提供一個(gè)渠道列表,開(kāi)發(fā)同學(xué)會(huì)根據(jù)這些渠道相應(yīng)地生成等量的渠道包。隨著渠道越來(lái)越多,為了提高渠道打包的效率,因此催生了對(duì)多渠道打包的方式的研究。
??同樣在 Flutter的項(xiàng)目開(kāi)發(fā)中也涉及到這個(gè)問(wèn)題,在這里主要講怎么配置開(kāi)發(fā)包與生產(chǎn)包,并快速打出并運(yùn)行相應(yīng)的渠道包。
??在這里我將分別從Android 端、IOS 端、Flutter 端進(jìn)行講解配置。
Android 配置
對(duì)于android,我們只要在app gradle模塊中配置productFlavors即可,這里我們?cè)赽uild.gradle 中 android 下面定義了dev、production兩種flavors。
[...]
android {
[...]
flavorDimensions "app"
productFlavors {
dev {//development
dimension "app"
resValue "string", "app_name", "多渠道打包${defaultConfig.versionCode}" // 設(shè)置默認(rèn)的app_name
applicationId "${defaultConfig.applicationId}.dev"
manifestPlaceholders = [
QQ_APP_ID: "xxx",
CHANNEL_NAME: "dev",
LOCATION_APP_KEY : "xxx", /// 高德地圖key
]
}
production{
dimension "app"
}
}
}
[...]
配置非常簡(jiǎn)單,這里可以設(shè)置不同的應(yīng)用程序applicationId后綴,
同樣也可以設(shè)置不同flavors 的app_name,CHANNEL_NAME。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xx.xx">
<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 配置
對(duì)于 ios,我們只需要在ios / Flutter文件夾中為每一個(gè)flavor創(chuàng)建相應(yīng)的配置文件,就像Flutter中默認(rèn)預(yù)定義Debug.xcconfig和Release.xcconfig一樣,因?yàn)閜roduction正式包不用變包名與應(yīng)用名,所以這里就只創(chuàng)建 dev_debug.xcconfig、dev_release.xcconfig兩個(gè)文件,
并在dev flavor對(duì)應(yīng)的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文件添加您定義的變量
然后針對(duì)每個(gè) flavor 創(chuàng)建對(duì)應(yīng)的scheme
記得在對(duì)話框中勾選上 shared
現(xiàn)在選擇Runner項(xiàng)目并添加您需要選擇的配置作為之前創(chuàng)建的配置文件作為配置文件。
注意,對(duì)于每個(gè)以Release- [flavorName]和Debug- [flavorName]命名的falvor都有兩個(gè)配置。這里注意名字不要重復(fù)。
為了使在打包、發(fā)布時(shí)iOS應(yīng)用使用正確的配置,這里需要編輯scheme,將構(gòu)建配置設(shè)置為所需的配置:
Flutter 配置
將main.dart重命名為main_common.dart,把公共配置、運(yùn)行部分定義在這里,然后創(chuàng)建 main_dev.dart和main_production.dart文件,引入main_common,根據(jù)需要在main_dev.dart和main_production.dart文件設(shè)置不同的配置參數(shù)。
可在 main_dev.dart 中設(shè)置網(wǎng)絡(luò)代理、以及第三方庫(kù)等的測(cè)試key。
import 'dart:io';
import 'package:dio/adapter.dart';
import 'package:flutter_common_utils/http/http_manager.dart';
import 'main_common.dart';
/// @desc 開(kāi)發(fā)、測(cè)試環(huán)境入口
/// @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;
};
};
///第三方庫(kù) 測(cè)試 key
}
在main_production.dar配置正式的第三方庫(kù) key等。
import 'main_common.dart';
/// @desc 正式環(huán)境入口
/// @time 2019-07-17 15:08
/// @author Cheney
Future<Null> main() async {
await initConfig();
///第三方庫(kù) 正式 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);
//初始化存儲(chǔ)管理
// await StorageUtil.getInstance();
}
///初始化 App
void initMaterialApp() {
HttpManager().init(
baseUrl: "xxx",
interceptors: [],
);
runApp(OKToast(child: HomeApp()));
}
這樣在命令行就可以分渠道運(yùn)行了:
flutter build --flavor dev -t lib/main-dev.dart
flutter build --flavor production -t lib/main-production.dart
如果你想直接大 IDE中直接分渠道包運(yùn)行,則要編輯配置創(chuàng)建兩個(gè)對(duì)應(yīng)的運(yùn)行配置:
這里創(chuàng)建了兩個(gè) flutter 運(yùn)行配置項(xiàng),main_dev、main_production。
在Dart entrypoint 中選擇上面創(chuàng)建的對(duì)應(yīng)的 main_dev.dart、dart_production.dart。
在Build flavor 中選擇對(duì)應(yīng)的 flavor(dev、production)。
此時(shí)在運(yùn)行對(duì)應(yīng)的main_dev、main_production就可以了。
最后
??如果在使用過(guò)程遇到問(wèn)題,歡迎下方留言交流。