Android 多渠道打包實(shí)操(更改包名、圖標(biāo)、主題資源 、替換常量、第三方SDK Appkey配置)

最近在做一個(gè)通用版的會(huì)員系統(tǒng),給予不同的公司使用,先前是通過切換版本分支來管理的,后面發(fā)現(xiàn)實(shí)在是繁瑣和痛苦管理,僅僅是需要更改不同的常量、主題資源、包名、圖標(biāo)等等,主體代碼邏輯功能基本不變。

先前了解過多渠道包的使用,其實(shí)這里完全可以通過 Gradle 的多渠道打包來這個(gè)痛點(diǎn),期間也踩了坑,在這里做個(gè)記錄

目錄

一、通過 productFlavors 配置不同的渠道/環(huán)境

二、manifestPlaceholders 占位符使用

三、了解 ApplicationId 與 PackageName的區(qū)別

四、替換資源文件

五、打包和調(diào)試編譯安裝不同版本的渠道

以下為完整的實(shí)際項(xiàng)目配置這有兩個(gè)渠道等同于給兩家不同的公司會(huì)員 app 使用的配置

apply plugin: 'com.android.application'
def releaseTime() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
//加載本地文件
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.ablegenius.member"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 101
        versionName "1.0.101"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        multiDexEnabled true

        ndk {
            //選擇要添加的對(duì)應(yīng)cpu類型的.so庫。
            abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "arm64-v8a", "x86_64"
            //, 'mips', 'mips64'
        }

        // 渠道配置 gradle 3.0.0 以上需要有這個(gè)
        flavorDimensions "app"
    }

    signingConfigs {

        AblegeniusMemberConfig {
            //第一種:使用gradle直接簽名打包
            /*  keyAlias 'dongwang'
            keyPassword '123123'
            storeFile file('src/main/WineverzhudiStoreFile.jks')
            storePassword '123123'*/

            //第二種:為了保護(hù)簽名文件,把它放在local.properties中并在版本庫中排除
            // ,不把這些信息寫入到版本庫中(注意,此種方式簽名文件中不能有中文)

            storeFile file(properties.getProperty("keystroe_storeFile"))
            storePassword properties.getProperty("keystroe_storePassword")
            keyAlias properties.getProperty("keystroe_keyAlias")
            keyPassword properties.getProperty("keystroe_keyPassword")

            v2SigningEnabled false
        }

    }

    // 多渠道/多環(huán)境 的不同配置
    productFlavors {


        SatayKing {
            //此處的常量都會(huì)通過Gradle 在 BuildConfig.java 文件中生成 , 你可以直接在Class中使用 BuildConfig.XXXX 進(jìn)行使用
            // 每個(gè)環(huán)境包名不同
            applicationId "com.ablegenius.member.satayking"
            // 動(dòng)態(tài)添加 string.xml 字段;
            // 注意,如果在這添加,在 string.xml 不能有這個(gè)字段,會(huì)重名!!!這里使用資源文件覆蓋的方式來處理應(yīng)用名稱
//            resValue "string", "app_name", "沙嗲王會(huì)員x"
            resValue "bool", "auto_updates", 'false'
            // 動(dòng)態(tài)修改 常量 字段
            buildConfigField "String", "MAIN_H5_URL", '"https://xxxxxxx22/index.html"'
            //服務(wù)器請(qǐng)求地址
            buildConfigField "String", "SERVER_URL", '"https://cloudxxxx22/a"'
           //一些常量
            buildConfigField "String", "company", '"SatayKing"'
            buildConfigField "String", "serial", '"xxxxx"'
            buildConfigField "int", "ENVIRONMENTInt", '2'
            // 修改 AndroidManifest.xml 里渠道變量
            manifestPlaceholders = [CHANNEL_VALUE: "SatayKing"
                                    , app_icon   : "@mipmap/ic_launcher_shadiewang",
                                    //此方式可直接在 manifest 中通過 ${icon} 進(jìn)行占位引用; 或者在main同級(jí)中創(chuàng)建不同渠道后創(chuàng)建 res 資源文件
                                    icon         : "@mipmap/ic_launcher_shadiewang",
                                    //極光相關(guān)
                                    JPUSH_PKGNAME: applicationId,
                                    JPUSH_APPKEY : "xxxxxxx", //JPush上注冊(cè)的包名對(duì)應(yīng)的appkey.
                                    JPUSH_CHANNEL: "developer-default", //暫時(shí)填寫默認(rèn)值即可.
                                    //Google Map 相關(guān)
                                    GoogleMapKey : "AIzaSyCLJ9Gng-xxxxx",

            ]

        }

        WineverHK {

            dimension "app"

            applicationId "com.ablegenius.member.wineverzhudi"

//            resValue "string", "app_name", "築地日本料理"
            resValue "bool", "auto_updates", 'true'
            resValue "drawable", "isrRank", 'true'
         
            buildConfigField "String", "MAIN_H5_URL", '"http://xxxxindex.html"'
            buildConfigField "String", "SERVER_URL", '"http://cloud.xxxx/a"'

            buildConfigField "String", "company", '"WineverHK"'
            buildConfigField "String", "serial", '"xxxx"'


            manifestPlaceholders = [CHANNEL_VALUE: "WineverHK"
                                    , app_icon   : "@mipmap/ic_launcher_zhudi",
                                    icon         : "@mipmap/ic_launcher_zhudi",

                                    JPUSH_PKGNAME: applicationId,
                                    JPUSH_APPKEY : "247aef555a20e8836d1ac361", //JPush上注冊(cè)的包名對(duì)應(yīng)的appkey.
                                    JPUSH_CHANNEL: "developer-default", //暫時(shí)填寫默認(rèn)值即可.

                                    GoogleMapKey : "AIzaSyCtAVjIVmGdnP44W2Nk8DjCT_OJISYUVxA",
            ]
        }


    }

    buildTypes {
        release {
            // release模式下,不顯示log
            buildConfigField("boolean", "LOG_DEBUG", "false")
            // 為版本名添加后綴
//            versionNameSuffix "-relase"
            // 不開啟混淆
            minifyEnabled false
            // 移除無用的resource文件
            shrinkResources false
            // 開啟ZipAlign優(yōu)化
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            signingConfig signingConfigs.AblegeniusMemberConfig


        }

        debug {

            // debug模式下,顯示log
            buildConfigField("boolean", "LOG_DEBUG", "true")

            //為已經(jīng)存在的applicationId添加后綴
//            applicationIdSuffix ".debug"
            // 為版本名添加后綴
            versionNameSuffix "-debug"
            // 不開啟混淆
            minifyEnabled false
            // 不開啟ZipAlign優(yōu)化
            zipAlignEnabled false
            // 不移除無用的resource文件
            shrinkResources false

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.AblegeniusMemberConfig
        }
    }

    // 3.0 gradle 批量打包
    android.applicationVariants.all { variant ->
        variant.outputs.all {
            //輸出apk名稱為:渠道名_版本名_時(shí)間.apk
            outputFileName = "${variant.productFlavors[0].name}Member_v${defaultConfig.versionName}_${releaseTime()}.apk"
        }
    }

    sourceSets {
        SatayKing { res.srcDirs = ['src/SatayKing/res', 'src/SatayKing/res/'] }
        WineverHK { res.srcDirs = ['src/WineverHK/res', 'src/WineverHK/res/'] }
        main { res.srcDirs = ['src/main/res', 'src/main/res/'] }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation xxxx
}

一、通過 productFlavors 配置不同的渠道/環(huán)境

productFlavors {
    SatayKing {
        applicationId "com.ablegenius.member.satayking"
    }

    WineverHK {
        applicationId "com.ablegenius.member.wineverzhudi"
    }
}

 

這里注意,在 defaultConfig 中,大家應(yīng)該都是寫了個(gè)默認(rèn)的 applicationId 的。
經(jīng)測(cè)試,productFlavors 設(shè)置的不同環(huán)境包名會(huì)覆蓋 defaultConfig 里面的設(shè)置,
所以我們可以推測(cè),它執(zhí)行的順序應(yīng)該是先執(zhí)行默認(rèn)的,然后在執(zhí)行分渠道的,如果沖突,會(huì)覆蓋處理,這也很符合邏輯。

二、manifestPlaceholders 占位符使用

項(xiàng)目中使用到了極光、GoogleMap 等第三方SDK的配置,大家都知道極光推送需要根據(jù)不同的包名 JPush上注冊(cè)的包名對(duì)應(yīng)的appkey 的才能進(jìn)行推送,如何去修改呢?

使用 manifestPlaceholders 來 定義 【GoogleMapKey 】常量,
在 AndroidManifest.xml 中 使用 "${GoogleMapKey}" 來占位,

<application
android:icon="${icon}"
      android:label="${app_name}"
      xxxxx>
      <!--渠道配置-->
      <meta-data
          android:name="CHANNEL"
          android:value="${CHANNEL_VALUE}" />


      <!-- Google Map Key -->   
      <meta-data
          android:name="com.google.android.geo.API_KEY"
          android:value="${GoogleMapKey}" /> 

<!--  極光推送-->
    
      <!-- User defined. 用戶自定義的廣播接收器-->
      <receiver
          android:name="com.ablegenius.member.receiver.JpushReceiver"
          android:enabled="true">
          <!--android:process=":remote"廣播運(yùn)行在遠(yuǎn)端單獨(dú)進(jìn)程中 ,調(diào)試斷點(diǎn)無法執(zhí)行需要關(guān)閉 或者 debug時(shí)候選擇 remote ! -->
          <intent-filter>
            
            xxxxx
              <!--推送包名必須一致使用Gradle中的常量才是最終的 -->
              <category android:name="${applicationId}" />
          </intent-filter>
      </receiver>

</application>

此處的app名稱和圖標(biāo)都可以使用占位符的方式進(jìn)行引用,

Tpis:如果是這種方式修改應(yīng)用名稱,注意應(yīng)用名稱定義在外層,通過 resValue 定義的常量String 需要 先使用 單引號(hào) 里面再是字符串,'"應(yīng)用名稱"'
            resValue "string", "app_name", "築地日本料理"

三、了解 ApplicationId 與 PackageName的區(qū)別

調(diào)試和打包出來的名稱會(huì)以Gradle 中的 applicationId 為最終包名,在 Manifest中的并不是最終的會(huì)被修改,地圖在做key驗(yàn)證的時(shí)候填寫的包名應(yīng)該是ApplicationId ,而不是packageName

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

四、替換資源文件

每個(gè)應(yīng)用資源布局 主題樣式,啟動(dòng)頁圖標(biāo)、應(yīng)用名稱可能 不一樣,這時(shí)怎么做呢? Google 做法:

在 main 的同級(jí)目錄下創(chuàng)建以渠道名命名的文件夾,然后創(chuàng)建資源文件(路徑要與 main 中的一致),然后打包的時(shí)候 gradle 就會(huì)自己替換或者合并資源。 替換圖片和合并顏色的原理也相似。必須名稱統(tǒng)一使用!

在對(duì)應(yīng)的渠道文件夾中創(chuàng)建res 文件, 注意渠道文件夾 目錄為main 同級(jí)中, 創(chuàng)建 res為 : src/渠道名稱/res

image.png

五、打包和調(diào)試編譯安裝不同版本的渠道

選取不同的渠道,Gradle 會(huì)自動(dòng)編譯指定渠道,然后再運(yùn)行項(xiàng)目即可

image.png

多渠道打包后很多渠道時(shí) 需要默認(rèn) 安裝哪個(gè)渠道, 需要 在Build 中做切換

也可以通過命令打包: ./gradlew assembleRelease

最后如果你有涉及到第三方的Appkey之類的一定要檢查好這塊,以及配置的SHA1值等

參考:

Gradle多渠道打包(動(dòng)態(tài)設(shè)定App名稱,應(yīng)用圖標(biāo),替換常量,更改包名,變更渠道)

Gradle 實(shí)現(xiàn) Android 多渠道定制化打包

ApplicationId 與 PackageName的區(qū)別

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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