Android Gradle多渠道打包

目录

1.多渠道打包是什么

多聚道打包指的是同一个应用生成多个不同的安装包,通常是APK文件,每个安装包可以包含不同的配置,资源。

2.为什么需要多渠道打包

1.数据统计:根据渠道区分来源,统计各个渠道的下载量和覆盖率;

2.精细化运营:根据数据分析来做营销和推广,提高应用的曝光

3.产品适配:需要适配不同厂商的系统API

3.多渠道配置

Variant

Variant是变体的意思,变体是指应用可以构建不同的版本,比如国内版和海外版,免费版和企业版等。

变体由多个构建类型组合而成,例如debug与release,以及构建脚本中定义的产品变种。

productFlavors

productFlavors翻译过来就是产品变种,用来定义不同的变体,每个变体可以有不同的配置和资源,最终打出来的包也不一样·

以华为、oppo为例子,在app/build.gradle文件中配置多渠道:

bash 复制代码
    flavorDimensions = ["version"]
    productFlavors {
        oppo {
            dimension = 'version'
            applicationId = 'com.example.gradlestudy.oppo'
            versionName = '1.0'
            versionCode = 1
        }
        huawei {
            dimension = 'version'
            applicationId = 'com.example.gradlestudy.huawei'
            versionName = '1.0'
            versionCode = 1
        }
    }

在productFlavors中通过create来创建渠道,并在渠道中按需配置属性参数。

比如applicationId所有渠道保持一致,这样可以保证一个设备只有一个应用安装,反正,如果想在一个设备上安装多个应用版本,也可以通过多渠道的方式来实现。

在build.gradle中defaultConfig{}中的配置为应用默认配置,都可以在渠道配置productFlavors{}中进行覆写和追加。

falvorDimensions表示产品变种的维度(Dimensions),是组的概念,同一个维度即为一个产品变种组,这里定义的是[version],名字可以自定义,也可以增加多个。

比如增加一个维度channel,比如要发布到Google Play和Amazon应用商店,这两个渠道可能有不同的品牌要求。

bash 复制代码
 flavorDimensions = ["version"]+["channel"]
    productFlavors {
        create("oppo") {
            dimension = 'version'
            applicationId = 'com.example.gradlestudy.oppo'
            versionName = '1.0'
            versionCode = 1
        }
        create("huawei") {
            dimension = 'version'
            applicationId = 'com.example.gradlestudy.huawei'
            versionName = '1.0'
            versionCode = 1
        }
        create("googlePlay") {
            dimension "channel"
            applicationId "com.example.gradlestudy.googleplay"
            versionName = '1.0'
            versionCode = 1
        }
        create("amazon") {
            dimension "channel"
            applicationId "com.example.gradlestudy.amazon"
            versionName = '1.0'
            versionCode = 1
        }
    }

每个维度的产品变种可以相互结合,生成不同的构建变体。例如,定义两个维度version和channel,每一个维度中包含两个产品风味,2*4=8的构建变体。

buildTypes

buildTypes是构建类型,用来定义构建类型配置,比如是否开启混淆,是否开启调试,通常包含debug和release两种类型。

bash 复制代码
    buildTypes {
        getByName("debug") {
            isDebuggable = true
        }
        getByName("release") {
            isMinifyEnabled = false
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
        }
    }

在多渠道配置中,构建类型和产品变种(productFlavors)一起使用,可以形成不同组合的构建变体(Variants)

3.构建变体组合

在配置完多渠道之后,打包方式跟平常的Run的有些区别,以前是单一的构建变体,现在是多组合的构建变体。

在AndroidStudio右侧打开Gradle面板,在build目录下可以看到具体的构建选项。

assemble是最全的构建方式,也就说所有的构建变体组合都会执行打包操作

assembleDebug会一次性打出4个包,比如执行assembleDebug会打出huaweiAmazonDebug,oppoAmazonDebug,huaweiGooglePlayDebug,oppoGooglePlayDebug

除了从Gradle面板选择任务执行之外,也可以使用命令行执行,例如:

kotlin 复制代码
./gradlew assembleDebug
// or 
./gradlew assembleHuawei

关于组合

现在的构建配置是:

  • 2个维度(flavorDimensions): version,channel
  • 4个产品变种(productFlavors):oppo,huawei,googlePlay,amazon
  • 2个构建类型(buildTypes):debug,release

会构成2x4x2的组合

4.渠道过滤

在某些情况下,想在渠道构建中去掉某个渠道,这个时候可以创建变体过滤器来移除它。

java 复制代码
    variantFilter{
        variant ->
            def flavors = variant.getFlavors()*.name // 获取所有维度的风味名称列表
            def buildTypeName = variant.buildType.name
            println("names-------> ${variant.flavors*.name}----${variant.buildType.name}")
            if(flavors.contains("googlePlay") && flavors.contains("oppo") && buildTypeName == "debug"){
                println "忽略变体: ${flavors.join('-')}-${buildTypeName}"
                setIgnore(true)
            }
    }

可以看到Build Variants中不会再显示oppoGooglePlayDebug变体

  • 使用 variant.getFlavors() 获取所有维度的产品风味名称。
  • 使用 variant.buildType.name 获取构建类型名称。
  • 根据条件调用 setIgnore(true) 忽略不需要的变体。

5.渠道资源

随着市场多元化的发展,很多应用也出现了企业版,海外版,极速版等多种版本,对于渠道配置的定制诉求也越来越多,比如资源文件(文本、图片),以及代码

资源文件

以常见的App名称和Icon图标为例,默认在AndroidManifest.xml文件中的application标签中有如下配置:

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        >
    </application>
</manifest>

android:icon和android:label分别引用的是app/src/main/目录下的mipmap图片资源和文本资源

这里的app/src/main目录为主目录,main相当于默认渠道,也可以新增其他渠道目录。

以huawei版为例子,在app/build.gradle文件中配置如下:

  • res文件夹下新增values文件夹,values文件夹下新增strings.xml文件,并配置华为版的应用名称
  • res文件夹下新增drawable和mipmap文件夹,并放置华为版的应用图标;
java 复制代码
<resources>
    <string name="app_name">GradleStudy huawei</string>
</resources>

这样在构建渠道变体华为的时候,Gradle会根据构建变体来找对应的目录文件,也就是说AndroidManifest.xml文件的android:icon和android:label引用的资源路径会从原来的main目录变为huawei目录。

在Build Variants中选择huaweiDebug变体

点击run查看运行效果

可以发现app应用图标和名字都换位huawei目录下对应的资源文件。

资源合并规则

  • 渠道构建时,渠道变体(huawei)会跟主变体(main)目录下的资源进行合并
    -如有同名配置资源,例如strings.xml文件中的app_name,则优先取渠道(huawei)配置资源进行覆盖,其他不同名的则进行合并;
  • layout文件、assets文件则是替换,渠道资源(huawei)优先于主变体(main)资源;

代码文件

代码文件不支持合并也不支持同名

比如在huawei渠道新增一个huaweiBean类,然后在main目录下也新增一个huaweiBean类,则会出现同名异常。

渠道中的代码为该渠道专属,只有在该渠道编译时才会与主变体main中的代码进行融合。

比如当切到oppoDebug渠道变体时,在main中就无法找到HuaweiBean类

如果oppo渠道想复用huawei渠道的代码可以使用sourceSets了。

SourceSets

sourceSets可以为渠道指定代码路径,以及res,manifest等资源文件路径。

如果oppo渠道想要复用huawei渠道的代码,我们就可以使用sourceSets来进行设置。

在app/build.gradle文件中配置如下:

java 复制代码
    sourceSets {
        oppo {
            res {
                srcDirs 'src\\oppo\\res'
                java.srcDirs("src/oppo/java", "src/huawei/java")
            }
        }
    }

在oppo渠道配置中,指定代码路径,这样,在oppo渠道变体进行编译时,就会把src/huawei/java目录下的代码进行融合,从而实现复用huawei渠道代码的功能。

除了指定java,res目录下的代码文件之外,其他资源代码文件也是支持的,可以按需指定多个目录。

java 复制代码
    sourceSets {
       oppo {
            res {
            java.srcDirs("src/oppo/java", "src/huawei/java")
            kotlin.srcDirs("src/huawei/kotlin")
            aidl.srcDirs("src/huawei/aidl")
            res.srcDirs("src/huawei/res")
            assets.srcDirs("src/huawei/assets")
            jniLibs.srcDirs("src/huawei/jniLibs")
            renderscript.srcDirs("src/huawei/rs")
            manifest.srcFile("src/huawei/AndroidManifest.xml")
        }
    }
    }

6. 渠道依赖项

除了资源文件和代码文件之外,我们的依赖可能会根据渠道有所不同,比如在做推送功能的时候,在打华为渠道包的时候,只依赖华为的推送,而不依赖oppo的推送,也就是根据渠道来配置依赖项。

当我们配置了渠道就会有对应的变体,[变体]+[依赖方式]就是渠道特有的依赖了。

以华为推送为例

java 复制代码
dependencies {
huaweiImplementation("com.huawei.hms:push:6.11.0.300")
}

多渠道依赖方式:

  • 默认依赖:implementation
  • 渠道依赖:变体+Implementation,如huaweiImplementation
  • 构建类型:类型+Implementation,如debugImplementation
  • 组合变体:变体+类型+Implementation,如huaweiDebugImplementation

7.渠道统计

通过在打包前预置渠道信息,然后在运行时获取并上报,从而实现渠道的数据统计。

有两种方式

meta-data

meta-data标签通常在AndroidManifest.xml文件中使用,通过键值对的方式为组件提供附加配置信息。

常见的第三方渠道统计比如友盟,会在AndroidManifest.xml中使用标签通过占位符的方式,来存储渠道信息。

配置meta-data:

java 复制代码
       <meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL_NAME}"/>

配置占位符:

java 复制代码
    productFlavors {
        create("oppo") {
            manifestPlaceholders["UMENG_CHANNEL_NAME"] = "oppo"
        }
        create("huawei") {
            manifestPlaceholders["UMENG_CHANNEL_NAME"] = "huawei"
        }
        }

可以通过manifestPlaceholders来替换AndroidManifest.xml文件中的value的值,确保UMENG_CHANNEL_NAME要对应上。

获取渠道信息的方法:

通过PackageManager获取并读取meta-data信息

java 复制代码
    fun getChannel(context:Context):String?{
        try {
            val applicationInfo = context.packageManager.getApplicationInfo(context.packageName,PackageManager.GET_META_DATA)
            if(applicationInfo.metaData!=null){
                return applicationInfo.metaData.getString("UMENG_CHANNEL")
            }
        }catch (e:PackageManager.NameNotFoundException){
            //处理异常
        }
        return null
    }

BuildConfig

BuildConfig通常用来存储一些常量信息,比如版本号,或者在buildTypes中根据构建环境来定义接口请求的域名地址等,BuildConfig会在编译时生成class文件。实际上在配置多渠道信息的时候,已经默认在BuildConfig中注入渠道信息了。

获取渠道:

调用BuildConfig.FLAVOR

如果想要自定义渠道信息,比如增加渠道号,也可以通过BuildConfig来存储

在Gradle8 +版本中,需要开启BuildConfig功能

java 复制代码
    buildFeatures{
        buildConfig = true
    }
java 复制代码
    productFlavors {
        create("oppo") {
            buildConfigField("int","CHANNEL_CODE","1001")
        }
        create("huawei") {
            buildConfigField("int","CHANNEL_CODE","1002")
        }
        }

配置完后重新运行就可以看到对应生成key的常量了

8.管理渠道

当渠道配置越来越多的时候,app目录下的build.gradle文件就会显得有些不易阅读和维护,这时候可以将配置模块化,把渠道相关配置抽成一个channel.gradle文件,然后在app/build.gradle文件中apply依赖进来,这样可以更好的管理和维护渠道项目的渠道配置,app/build.gradle文件也会少一些。

在项目根目录中新建channel.gradle文件,并配置如下:

java 复制代码
android{
    flavorDimensions = ["version"]
    productFlavors {
        create("oppo") {
            dimension = 'version'
            applicationId = 'com.example.gradlestudy.oppo'
            versionName = '1.0'
            versionCode = 1
            manifestPlaceholders["UMENG_CHANNEL_NAME"] = "oppo"
            buildConfigField("int", "CHANNEL_CODE", "1001")
        }
        create("huawei") {
            dimension = 'version'
            applicationId = 'com.example.gradlestudy.huawei'
            versionName = '1.0'
            versionCode = 1
            manifestPlaceholders["UMENG_CHANNEL_NAME"] = "huawei"
            buildConfigField("int", "CHANNEL_CODE", "1002")
        }
    }
    sourceSets {
        huawei {
            res {
                srcDirs 'src\\huawei\\res'
            }
        }
        getByName("oppo") {
            res {
                srcDirs 'src\\oppo\\res'
                //java.srcDirs("src/oppo/java", "src/huawei/java")
            }
        }
    }
}
java 复制代码
apply from:"../channel.gradle"

重新sync即可

相关推荐
虽千万人 吾往矣36 分钟前
golang context源码
android·开发语言·golang
李新_2 小时前
Android 多进程并发控制如何实现
android·java
ll_god2 小时前
Android 应用wifi direct连接通信实现
android
氦客2 小时前
kotlin知识体系(五) :Android 协程全解析,从作用域到异常处理的全面指南
android·开发语言·kotlin·协程·coroutine·suspend·functions
IvanCodes5 小时前
MySQL 视图
android·数据库·sql·mysql·oracle
KevinWang_5 小时前
Java 和 Kotlin 混编导致的 bug
android·kotlin
好学人5 小时前
Android动画系统全面解析
android
leverge20095 小时前
android studio 运行java main报错
android·ide·android studio
RichardLai885 小时前
Flutter 环境搭建
android·flutter