Android打包之多版本、多環(huán)境附较、多渠道

在平時(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末践瓷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子亡蓉,更是在濱河造成了極大的恐慌晕翠,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砍濒,死亡現(xiàn)場(chǎng)離奇詭異淋肾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)爸邢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門樊卓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杠河,你說我怎么就攤上這事碌尔「弦矗” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵七扰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我陪白,道長(zhǎng)颈走,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任咱士,我火速辦了婚禮立由,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘序厉。我一直安慰自己锐膜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布弛房。 她就那樣靜靜地躺著道盏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪文捶。 梳的紋絲不亂的頭發(fā)上荷逞,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音粹排,去河邊找鬼种远。 笑死,一個(gè)胖子當(dāng)著我的面吹牛顽耳,可吹牛的內(nèi)容都是我干的坠敷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼射富,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼膝迎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胰耗,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤弄抬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宪郊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掂恕,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年弛槐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了懊亡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乎串,死狀恐怖店枣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤鸯两,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布闷旧,位于F島的核電站,受9級(jí)特大地震影響钧唐,放射性物質(zhì)發(fā)生泄漏忙灼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一钝侠、第九天 我趴在偏房一處隱蔽的房頂上張望该园。 院中可真熱鬧,春花似錦帅韧、人聲如沸里初。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽双妨。三九已至,卻和暖如春叮阅,著一層夾襖步出監(jiān)牢的瞬間斥难,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工帘饶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哑诊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓及刻,卻偏偏與公主長(zhǎng)得像镀裤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缴饭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容