在平時(shí)的Android開發(fā)中吃粒,我們經(jīng)常會(huì)遇到在不同網(wǎng)絡(luò)環(huán)境(比如:開發(fā)環(huán)境、測(cè)試環(huán)境)之間的切換拒课、一次打多個(gè)渠道包等需求徐勃,如何優(yōu)雅的管理網(wǎng)絡(luò)環(huán)境的配置?如何快速的打出多個(gè)渠道包捕发?這是一個(gè)值得研究的問題疏旨。
如果每一次在不同網(wǎng)絡(luò)環(huán)境間切換很魂,都需要更改代碼扎酷,然而重新打包,那未免有點(diǎn)低效遏匆。下面是我的實(shí)踐探索法挨,看網(wǎng)上很多人都是根據(jù)buildType來切換網(wǎng)絡(luò)環(huán)境谁榜,感覺有點(diǎn)不好,因?yàn)榫W(wǎng)絡(luò)環(huán)境可能很多種凡纳,而buildType我們一般是2種窃植,而且,不同網(wǎng)絡(luò)環(huán)境的包最好能同時(shí)安裝在手機(jī)上荐糜,以便我們調(diào)試巷怜。最好,我一看這個(gè)包的名稱和圖標(biāo)暴氏,就能知道這是什么環(huán)境的包延塑。
一、概述
1.多版本
基于buildTypes
(1)debug:調(diào)試版本,無混淆
(2)release:發(fā)布版本,有混淆答渔、壓縮
2.多環(huán)境
基于productFlavors
(1)develop:開發(fā)環(huán)境关带,開發(fā)和自測(cè)時(shí)使用
(2)check:測(cè)試環(huán)境,克隆一份生產(chǎn)環(huán)境的配置沼撕,在這里測(cè)試通過后宋雏,再發(fā)布到生產(chǎn)環(huán)境。
之所以沒命名為test是因?yàn)樵趃radle編譯時(shí):ProductFlavor names cannot start with 'test'
(3)product:生產(chǎn)環(huán)境务豺,正式提供服務(wù)的磨总。
3.多渠道
基于Android新的應(yīng)用簽名方案APK Signature Scheme v2中的APK Signing Block區(qū)塊
我這里使用的是美團(tuán)封裝的Walle庫(kù)。使用Walle庫(kù)請(qǐng)確保你的Android Gradle 插件版本在2.2.0以上冲呢。
為什么不直接使用productFlavors來打包多渠道舍败?因?yàn)閜roductFlavors打多渠道包太慢了,打30個(gè)包差不多十幾分鐘敬拓,無法忍受邻薯!
為什么不使用美團(tuán)之前基于META-INF進(jìn)行渠道標(biāo)識(shí)的方案?因?yàn)锳ndroid7.0之后的這種黑科技已經(jīng)失效了乘凸!
二厕诡、示例
1、配置build.gradle
(1) 在位于項(xiàng)目的根目錄 build.gradle 文件中添加Walle Gradle插件的依賴营勤, 如下:
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.meituan.android.walle:plugin:1.0.3'
}
}
(2) 在當(dāng)前App的 build.gradle 文件中apply這個(gè)插件灵嫌,并添加上用于讀取渠道號(hào)的aar
apply plugin: 'com.android.application'
apply plugin: 'walle'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
keyAlias KEY_ALIAS
keyPassword KEY_PASSWORD
storeFile rootProject.file(KEYSTORE_FILE)
storePassword KEYSTORE_PASSWORD
}
}
buildTypes {
//調(diào)試版本,無混淆
debug {
minifyEnabled false
signingConfig signingConfigs.release
}
//發(fā)布版本葛作,有混淆
release {
minifyEnabled true
zipAlignEnabled true
shrinkResources true
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
//開發(fā)環(huán)境
develop {
buildConfigField "int", "ENV_TYPE", "1"
applicationId 'om.soubu.walledemo.develop'
manifestPlaceholders = [
app_name: "開-WalleDemo",
app_icon: "@drawable/icon_develop"
]
}
//測(cè)試環(huán)境
check {
buildConfigField "int", "ENV_TYPE", "2"
applicationId 'om.soubu.walledemo.check'
manifestPlaceholders = [
app_name: "測(cè)-WalleDemo",
app_icon: "@drawable/icon_check"
]
}
//生產(chǎn)環(huán)境
product {
buildConfigField "int", "ENV_TYPE", "3"
applicationId 'com.soubu.walledemo.product'
manifestPlaceholders = [
app_name: "WalleDemo",
app_icon: "@drawable/icon_product"
]
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.0'
testCompile 'junit:junit:4.12'
compile 'com.meituan.android.walle:library:1.0.3'
}
(3) 這里寿羞,我根據(jù)不同的環(huán)境生成了不同包名的apk,方便在手機(jī)上同時(shí)安裝多個(gè)環(huán)境的應(yīng)用赂蠢。為了讓gradle動(dòng)態(tài)更改apk的名稱和圖標(biāo)绪穆,我們需要在manifest文件中使用${app_icon}、${app_name}等占位符
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.soubu.walledemo">
<application
android:allowBackup="true"
android:icon="${app_icon}"
android:label="${app_name}"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
(4) 在代碼中獲取多渠道信息
String channel = WalleChannelReader.getChannel(getApplicationContext());
(5) 在代碼中獲取多環(huán)境信息
int envType = BuildConfig.ENV_TYPE;
這里的BuildConfig是由gradle動(dòng)態(tài)生成的:
package com.soubu.walledemo;
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "om.soubu.walledemo.develop";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "develop";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Fields from product flavor: develop
public static final int ENV_TYPE = 1;
}
而ENV_TYPE這個(gè)字段其實(shí)就來自于我們的build.gradle:
productFlavors {
//開發(fā)環(huán)境
develop {
buildConfigField "int", "ENV_TYPE", "1"
applicationId 'om.soubu.walledemo.develop'
manifestPlaceholders = [
app_name: "開-WalleDemo",
app_icon: "@drawable/icon_develop"
]
}
{
這里我們最好定義一個(gè)常量類區(qū)分這些環(huán)境的類型:
public class EnvType {
public static final int DEVELOP = 1;//開發(fā)環(huán)境
public static final int CHECK = 2;//測(cè)試環(huán)境
public static final int PRODUCT = 3;//正式環(huán)境
}
2、打包多環(huán)境
這里我們直接執(zhí)行assemble命令玖院,打包所有的buildType*productFlavors
或者使用命令行也可以:
gradle assemble
執(zhí)行結(jié)果:26秒搞定6個(gè)包:2個(gè)版本*3個(gè)環(huán)境
這里我們可以看到debug包都是1.4M,而release包都是0.7M菠红,顯然,我們的混淆和壓縮配置是生效了的难菌,雖然這里我并沒寫混淆規(guī)則
我們分別安裝3個(gè)環(huán)境的包到自己的手機(jī)上:
看三個(gè)包的名稱和圖標(biāo)都不一樣试溯,顯然我們之前在manifest文件中配置的占位符生效了。
然后我們點(diǎn)進(jìn)去分別看看這3個(gè)app的區(qū)別:
這樣郊酒,我們就可以在代碼中遇绞,根據(jù)環(huán)境字段envType的不同,來選擇不同的網(wǎng)絡(luò)環(huán)境了燎窘。
界面的代碼如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tvEnv = (TextView) findViewById(R.id.tv_env);
TextView tvChannel = (TextView) findViewById(R.id.tv_channel);
TextView tvPackage = (TextView) findViewById(R.id.tv_package);
String channel = WalleChannelReader.getChannel(this.getApplicationContext());
int envType = BuildConfig.ENV_TYPE;
String packageName = getPackageName();
switch (envType) {
case EnvType.DEVELOP:
tvEnv.setText("envType=" + "開發(fā)環(huán)境");
break;
case EnvType.CHECK:
tvEnv.setText("envType=" + "測(cè)試環(huán)境");
break;
case EnvType.PRODUCT:
tvEnv.setText("envType=" + "生產(chǎn)環(huán)境");
break;
}
tvChannel.setText("channel=" + channel);
tvPackage.setText("package=" + packageName);
}
}
3试读、打包多渠道
在Project的根目錄下新建channel文件:
anzhi #安智
baidu #百度
huawei #華為
oppo #oppo
wdj #豌豆莢
xiaomi #小米
yyb #應(yīng)用寶
執(zhí)行g(shù)radle命令:
(1) 打包文件內(nèi)的渠道包
gradle assembleProductRelease -PchannelFile=channel
(2) 打包自定義數(shù)組內(nèi)的渠道包
gradle assembleProductRelease -PchannelList=qihu,vivo,lenovo
關(guān)于Walle庫(kù)的更多使用:詳見Github-walle
運(yùn)行結(jié)果:17秒搞定8個(gè)包:1個(gè)默認(rèn)包+7個(gè)渠道包
最后,奉上源碼:WalleDemo
常見問題
1荠耽、找不到簽名文件的配置钩骇?
汗,因?yàn)槲业腄emo中并沒有上傳我的jks文件铝量,你可以添加自己的jks文件倘屹,然后在gradle.properties里面配置好簽名文件的密碼即可
在gradle.properties添加簽名文件的配置key-value
在build.gradle中引用配置的key
2、develop慢叨、check纽匙、product,如果直接run代碼拍谐,怎么設(shè)置默認(rèn)的環(huán)境烛缔?
點(diǎn)擊查看AndroidStudio左下角的BuildVariants,然后選擇設(shè)置默認(rèn)的run環(huán)境即可轩拨。
BuildVariants= buildTypes* productFlavors