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即可

相关推荐
断剑重铸之日19 分钟前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安21 分钟前
Android Library Maven 发布完整流程指南
android
岁月玲珑27 分钟前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟5 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡6 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi006 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil8 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你8 小时前
Android View的绘制原理详解
android
移动开发者1号11 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号11 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin