在平時的Android開發中,咱們常常會遇到在不一樣網絡環境(好比:開發環境、測試環境)之間的切換、一次打多個渠道包等需求,如何優雅的管理網絡環境的配置?如何快速的打出多個渠道包?這是一個值得研究的問題。
若是每一次在不一樣網絡環境間切換,都須要更改代碼,然而從新打包,那未免有點低效。下面是個人實踐探索,看網上不少人都是根據buildType來切換網絡環境,感受有點很差,由於網絡環境可能不少種,而buildType咱們通常是2種,並且,不一樣網絡環境的包最好能同時安裝在手機上,以便咱們調試。最好,我一看這個包的名稱和圖標,就能知道這是什麼環境的包。android
基於buildTypesgit
(1)debug:調試版本,無混淆
(2)release:發佈版本,有混淆、壓縮github
基於productFlavors數組
(1)develop:開發環境,開發和自測時使用
(2)check:測試環境,克隆一份生產環境的配置,在這裏測試經過後,再發布到生產環境。
之因此沒命名爲test是由於在gradle編譯時:ProductFlavor names cannot start with 'test'
(3)product:生產環境,正式提供服務的。網絡
基於Android新的應用簽名方案APK Signature Scheme v2中的APK Signing Block區塊app
我這裏使用的是美團封裝的Walle庫。使用Walle庫請確保你的Android Gradle 插件版本在2.2.0以上。ide
爲何不直接使用productFlavors來打包多渠道?由於productFlavors打多渠道包太慢了,打30個包差很少十幾分鍾,沒法忍受!測試
爲何不使用美團以前基於META-INF進行渠道標識的方案?由於Android7.0以後的這種黑科技已經失效了!gradle
(1) 在位於項目的根目錄 build.gradle 文件中添加Walle Gradle插件的依賴, 以下:ui
buildscript { dependencies { classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.meituan.android.walle:plugin:1.0.3' } }
(2) 在當前App的 build.gradle 文件中apply這個插件,並添加上用於讀取渠道號的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 { //調試版本,無混淆 debug { minifyEnabled false signingConfig signingConfigs.release } //發佈版本,有混淆 release { minifyEnabled true zipAlignEnabled true shrinkResources true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } productFlavors { //開發環境 develop { buildConfigField "int", "ENV_TYPE", "1" applicationId 'om.soubu.walledemo.develop' manifestPlaceholders = [ app_name: "開-WalleDemo", app_icon: "@drawable/icon_develop" ] } //測試環境 check { buildConfigField "int", "ENV_TYPE", "2" applicationId 'om.soubu.walledemo.check' manifestPlaceholders = [ app_name: "測-WalleDemo", app_icon: "@drawable/icon_check" ] } //生產環境 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) 這裏,我根據不一樣的環境生成了不一樣包名的apk,方便在手機上同時安裝多個環境的應用。爲了讓gradle動態更改apk的名稱和圖標,咱們須要在manifest文件中使用{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) 在代碼中獲取多環境信息
int envType = BuildConfig.ENV_TYPE;
這裏的BuildConfig是由gradle動態生成的:
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這個字段其實就來自於咱們的build.gradle:
productFlavors { //開發環境 develop { buildConfigField "int", "ENV_TYPE", "1" applicationId 'om.soubu.walledemo.develop' manifestPlaceholders = [ app_name: "開-WalleDemo", app_icon: "@drawable/icon_develop" ] } {
這裏咱們最好定義一個常量類區分這些環境的類型:
public class EnvType { public static final int DEVELOP = 1;//開發環境 public static final int CHECK = 2;//測試環境 public static final int PRODUCT = 3;//正式環境 }
這裏咱們直接執行assemble命令,打包全部的buildType*productFlavors
或者使用命令行也能夠:
gradle assemble
執行結果:26秒搞定6個包:2個版本*3個環境
這裏咱們能夠看到debug包都是1.4M,而release包都是0.7M,顯然,咱們的混淆和壓縮配置是生效了的,雖然這裏我並沒寫混淆規則
咱們分別安裝3個環境的包到本身的手機上:
看三個包的名稱和圖標都不同,顯然咱們以前在manifest文件中配置的佔位符生效了。
而後咱們點進去分別看看這3個app的區別:
這樣,咱們就能夠在代碼中,根據環境字段envType的不一樣,來選擇不一樣的網絡環境了。
界面的代碼以下:
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=" + "開發環境"); break; case EnvType.CHECK: tvEnv.setText("envType=" + "測試環境"); break; case EnvType.PRODUCT: tvEnv.setText("envType=" + "生產環境"); break; } tvChannel.setText("channel=" + channel); tvPackage.setText("package=" + packageName); } }
在Project的根目錄下新建channel文件:
anzhi #安智 baidu #百度 huawei #華爲 oppo #oppo wdj #豌豆莢 xiaomi #小米 yyb #應用寶
執行gradle命令:
(1) 打包文件內的渠道包
gradle assembleProductRelease -PchannelFile=channel
(2) 打包自定義數組內的渠道包
gradle assembleProductRelease -PchannelList=qihu,vivo,lenovo
關於Walle庫的更多使用:詳見Github-walle
運行結果:17秒搞定8個包:1個默認包+7個渠道包
最後,奉上源碼:WalleDemo
一、找不到簽名文件的配置?
汗,由於個人Demo中並無上傳個人jks文件,你能夠添加本身的jks文件,而後在gradle.properties裏面配置好籤名文件的密碼便可
在gradle.properties添加簽名文件的配置key-value
在build.gradle中引用配置的key
二、develop、check、product,若是直接run代碼,怎麼設置默認的環境?
點擊查看AndroidStudio左下角的BuildVariants,而後選擇設置默認的run環境便可。
BuildVariants= buildTypes* productFlavors