Gradle配置Build多渠道包

什么是多渠道包

渠道包就是要在安装包apk中添加渠道信息,也就是channel,对应不同的渠道,例如:小米市场、360市场、应用宝市场等。需要为每个apk包设定一个可以区分渠道的标识,这个为apk包设定应用市场标识的过程就是多渠道打包。

为什么要提供多渠道包

国内存在着有众多的应用市场,产品在不同的渠道可能有不同的统计需求,为此Android开发人员需要为每个应用市场发布一个安装包,在安装包中添加不同的标识,应用在请求网络的时候携带渠道信息,方便后台做运营统计。

通过配置gradle实现多渠道打包的方式

其核心原理就是在编译时通过gradle修改AndroidManifest.xml中的meta-data占位符的内容,执行N次打包流程,然后就可以在java中通过API获取对应的数据。

BuildTypes、Flavors、BuildVariants

1. BuildTypes : 构建类型,gradle组件默认提供给了debug,release构件类型。

buildTypes {
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.Release
        zipAlignEnabled true
        setDebuggable false

    }

    debug {
        minifyEnabled false
        setDebuggable true
        testCoverageEnabled true
    }
}

2. Flavors : 产品渠道,可以根据productFlavors,针对不同的渠道生产个性化apk。

productFlavors {
    xiaomi {
        dimension EDITION
        buildConfigField "String", "PARTNER_CODE", "XM""
    }

    huawei {
        dimension EDITION
        buildConfigField "String", "PARTNER_CODE", "HW"" 
        //添加Config配置参数,项目中可以通过Config.PARTNER_CODE 获取
    }
}

3. BuildVariants:每一个buildtype和flavor组成一个buildvariant。

在Android对 Gradle 插件的扩展支持中,其中最常见的便是利用 variant 来对构建过程中各默认 task 进行 hook,关于 variants的类型,一共可分为3种类型

applicationVariants:只适用 app plugin

Manifest配置渠道占位符

<meta-data
            android:name="channel"
            android:value="${channel_value}"/> //yyb,360

app模块的build.gradle

android{
    //产品维度,不同维度用","分隔。下面维度为产品+型号组合
    flavorDimensions 'product', 'model'
    
    //productFlavors是android节点下的一个节点
    productFlavors {
    
        baidu {
            dimension 'product'
        }
        xiaomi {
            dimension 'product'
        }
        yyb {
            dimension 'product'
        }
    
        model1 {
            dimension 'model'
        }
    
        model2 {
            dimension 'model'
        }
    }
}
    //如果需要在不同渠道统一配置,使用productFlavors.all字段
    //替换manifest里面channel_value的值,
    //遍历productFlavors,他们的 channel_value 设置为自己的 name(baidu。xiaomi。。。)
    productFlavors.all { flavor ->
        manifestPlaceholders = [channel_value: name]}

    //设置渠道包名称-> howow-xiaomi-release-1.0.apk
    applicationVariants.all { variant ->
        variant.outputs.all { output ->
            //output即为该渠道最终的打包产物对象
            //设置它的outputFileName属性
            outputFileName = "hwow_${variant.productFlavors[0].name}_${variant.buildType.name}_${variant.versionName}.apk"
        }
    }
}

运行时获取渠道信息

private String getChannel() {
       PackageManager pm = getPackageManager();
       ApplicationInfo appInfo = pm.getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
       return appInfo.metaData.getString("channel_value");
}

libraryVariants:只适用 library plugin library构建变种。例如包名

libraryVariants.all { variant ->
    def editionName = ""
    def configName = ""
    def flavor
    variant.outputs.all { output ->
        productFlavors.each { productFlavor ->
            flavor = productFlavor
            switch (productFlavor.dimension) {
                case CONFIG:
                    configName = productFlavor.name
                    break
                case EDITION:
                    editionName = productFlavor.name
                    break
            }
        }

        //添加项目/仓库名称
        def fileNamePrefix = "${PROJECT_NAME}"

        //添加渠道/省份名称,垂直市场建议使用省份拼音简称命名
        switch (editionName) {
            case "market":
                fileNamePrefix += "-all"
                break
            default:
                fileNamePrefix += "-${editionName}"
                break
        }

        //添加partnerCode
        def partnerCode = "all"
        def partnerCodeField = flavor.getBuildConfigFields().get("PARTNER_CODE")
        if (partnerCodeField != null) {
            partnerCode = partnerCodeField.getValue()
            partnerCode = partnerCode.replace("\"", "")
        }

        fileNamePrefix += "-${partnerCode}"

        if (editionName == "xiaomi" ) {
            fileNamePrefix += "-multi"
        }

        //添加环境标识,online,uat等
        fileNamePrefix += "-online"

        //添加版本号
        fileNamePrefix += "-${android.defaultConfig.versionName}"

        //添加时间戳
        fileNamePrefix += "-${releaseTime()}"

        //添加功能标识,混淆
        if (buildType.minifyEnabled) {
            fileNamePrefix += "-obfuscated"
        }

        //添加签名标识
        if (buildType.name == "debug") {
            fileNamePrefix += "-debug"
        } else {
            fileNamePrefix += "-release"
        }

        println fileNamePrefix

        outputFileName = "${fileNamePrefix}.aar"
    }
}

testVariants:以上两者都适用

配置差异化的 logo 和 app名字,则可以在 gradle 中使用下面这段

android {
    flavorDimensions('abi', 'version')
    productFlavors {
        // 省略其他的风味配置 
        
        x86 {
            dimension 'abi'
            
            // 配置不同的包名,达到能两个风味能共存
            applicationId 'com.zinc.bear'
    
            manifestPlaceholders = [
                    logo    : "@drawable/logo_bear",
                    appName : "bear",
            ]
    
            // 配置签名
            signingConfig signingConfigs.jiangpengyong
        }
    
        armV7 {
            dimension 'abi'
    
            // 配置不同的包名,达到能两个风味能共存
            applicationId 'com.zinc.shark'
    
            manifestPlaceholders = [
                    logo    : "@drawable/logo_shark",
                    appName : "shark",
            ]
    
            // 配置签名
            signingConfig signingConfigs.xiaopenyou
        }
    }
}
然后在 AndroidManifest.xml 中使用,使用 ${你配置的变量名}
<application
        android:allowBackup="true"
        android:icon="${logo}"
        android:label="${appName}"
        android:roundIcon="${logo}"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
......

但是,这种方式存在一些缺点:

1.每生成一个渠道包,都要重新执行一遍构建流程,效率太低,只适用于渠道较少的场景。
2.Gradle会为每个渠道包生成一个不同的BuildConfig.java类,记录渠道信息,导致每个渠道包的dex的CRC值都不同

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "org.devio.as.proj.biz_home";
  public static final String BUILD_TYPE = "debug";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
  //增加了productflavor之后,这里的值,每个apk都不同
  public static final String FLAVOR = "";
}

如果使用了微信的Tinker热补丁方案,那么就需要为不同的渠道包打不同的补丁,因为Tinker是通过对比基础包base.apk和新包new.apk生成差分补丁patch.apk,然后再把补丁patch.apk和已安装的基础包old_base.apk一起合成新的apk。
这就要求用于生成差分补丁的基础包base.apk里面的DEX和已安装的基础包old_base.apk里面的DEX是完全一致的,就是说手机上安装的是应用宝渠道包,那必须使用应用宝渠道包来生成补丁patch.apk,才能合并成功)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值