多渠道打包笔记

多渠道打包笔记

多渠道打包是一种可以针对同一主体通过不同的配置差异,一次性输出不同结果的配置方式。灵活运用多渠道打包可以有效减少主体部分的开发和维护。而多渠道打包主要围绕productFlavors和flavorDimensions两个配置项。

风味配置productFlavors

在build.gradle的android内容中新增productFlavors即可实现多渠道配置,配置方式如下。

android{
    //其他配置信息
    	
    //产品风味(各渠道配置)
    productFlavors{
        coffee{
              //风味1配置
        }
        
        tea{
             //风味2配置
        }
    }
}

当module未配置多渠道打包时,相当于使用一种默认风味,而每个风味在编译目标的选择上studio会默认提供debug和release两种类型,如上示例,配置了coffee和tea两个渠道(风味)之后,在编译类型上将衍生出4中类型,分别是:coffeeDebug、coffeeRelease、teaDebug、teaRelease。

风味配置项与默认项相同时会覆盖默认项(defaultConfig ),其中可以覆盖的信息包括应用包名、版本号、版本名称、sdk版本、签名配置等等。

除此之外也可以在产品风味中定义差异化的manifest配置,其中包括应用名称、图标、样式等等任何一个manifest中的属性值。

1、包名配置
android{
    defaultConfig {
        applicationId "com.abc.demo"
        ...
    }
    //...
	
    //产品风味(各渠道配置)
    productFlavors{
        coffee{
              applicationId "com.abc.test"
        }
        ...
    }
}

编译出的coffeeXXX.apk(XXX表示Debug或者Release,下同)包名将会变成com.abc.test而不是com.abc.demo

2、applicationIdSuffix(包名后缀)
android{
    defaultConfig {
        applicationId "com.abc.demo"
        ...
    }
    //...
    	
    //产品风味(各渠道配置)
    productFlavors{
        coffee{
              applicationIdSuffix ".debug"
        }
        ...
    }
}

编译出的coffeeXXX.apk包名将会变成com.abc.test.debug

3、版本以及BuildConfig

versionCode、versionName、buildConfigField、minSdkVersion、targetSdkVersion

android {
    //其他...
    defaultConfig {
        applicationId "com.abc.test"
        minSdkVersion 24
        targetSdkVersion 31
        versionCode 1
        versionName "1.0"
    }
    
    productFlavors{
        coffee{
            versionCode 11
            versionName "11.0.0"
            buildConfigField "Integer", "UI_VERSION", "1"
        }
        tea{
            versionCode 20
            versionName "20.0.1"
            buildConfigField "Integer", "UI_VERSION", "2"
        }
   }
}

这里输出的目标apk中coffeeXXX.apk中versionCode将变成11,versionName将变成11.0.0,而该包没的BuildConfig.java中会新增一个整型(int)常量,名称为“UI_VERSION”,值为1。

BuildConfig.java类似于R.java自动生成的,包名R.java一样,位置为build/generated/source/configBuild/渠道名称XXX/res包对应的文件夹层级/BuildConfig.java

4、manifestPlaceholders(动态配置Manifest)

该配置可以在 AndroidManifest.xml 中替换的参数以达到不同应用图标、应用名称甚至其他应用属性等。比如:

productFlavors {
    coffee {
      versionName "coffee_${releaseTime()}"
      buildConfigField "Integer", "UI_VERSION", "1"
      manifestPlaceholders = [
           icon : "@drawable/icon_coffee",
           style : "@style/AppThemeNightCoffee",
           configChanges: "orientation|keyboard|screenSize|screenLayout|locale|layoutDirection",
           persistent : false,
           shareUid : "",
		   channelVale:"coffe_app"
         ]
      }

      tea {
         versionName "coffee_${releaseTime()}"
         buildConfigField "Integer", "UI_VERSION", "2"
         manifestPlaceholders = [
             icon : "@drawable/icon_tea",
             style : "@style/AppThemeLight",
             configChanges: "orientation|keyboard|screenSize|screenLayout|locale|layoutDirection|uiMode",
             persistent : false,
             shareUid : "android.uid.system",
		   	 channelVale:"tea_app"
           ]
    }
}

manifest中使用”${属性名}”的方式获取值,比如:

 <application
    android:icon="${icon}"
    android:persistent="${persistent}"
    android:theme="${style}"
    ...>
    <meta-data
        android:name="CHANNEL_NAME"
        android:value="${channelVale}" />

    <activity
        android:name=".MainActivity"
        android:configChanges="${configChanges}"
        android:icon="${icon}"
        ...>

这里在application添加meta-data信息可以实现在java中获取对应渠道信息的效果。比如这里想要在java中知道当前渠道是哪个那么可以在代码中获取meta-data值就可以了。

String channelName = null;
try {
    ApplicationInfo info = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
    channelName = info.metaData.getString("CHANNEL_NAME");
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
}
5、混淆配置

consumerProguardFiles主要作用于library中,包括aar或导入的library 。

作用是:负责该library被进行编译时的混淆规则,在主App 的模块下可以不用再管理各个 library 的混淆规则而直接使用各个 library 的混淆规则文件即可。如:

android{
    defaultConfig {
        ...
        consumerProguardFiles "consumer-rules.pro"
    }
    //产品风味(各渠道配置)
    productFlavors{
        teaLib{
              consumerProguardFiles "rules_1.pro"
        }
        ...
    }
}

如果需要多个文件配置可以使用consumerProguardFiles,该属性是一个 List 类型,如:

consumerProguardFiles "rules_1.pro","rules_2.pro"

proguardFiles主要作用在application编译时配置需要的混淆规则 。如果在productFlavors中有配置,那么在编译过程中将忽略掉默认buildType中的规则。如:

android{
     buildTypes {
            release {
                minifyEnabled true
                proguardFiles "proguard-rules.pro"
            }
             //其他...
        }    

    //产品风味(各渠道配置)
    productFlavors{
        tea{
              proguardFiles "proguard-rules.pro","rules_1.pro"
        }
        ...
    }
}
6、签名配置

signingConfig主要进行签名信息配置,该配置一般放在buildType中的debug或者release,如:

android{
    signingConfigs {
        release{
            storeFile file('key1.jks')
            storePassword '123456'
            keyAlias 'key0'
            keyPassword '123456'
        }
     }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
   //其他信息...
}

如果需要每个渠道有不同的签名配置可以使用如下方式:

1、定义签名信息

android{
	signingConfigs {
	        sign1{
	            ...
	        }
	
	        sign2 {
	          ...
	        }
	    }
	 ...
}

2、在各个风味中单独指定签名

android {
  productFlavors {
	   coffee{
	           signingConfig signingConfigs.sign1
	   }
	   tea{
	           signingConfig signingConfigs.sign2
	   }
	   ...
      }
      ...
}

以上方式均需要在同一纬度(风味纬度在后面有说明)的风味中替换,不同维度时(二维、三维等)无效

同时各个风味单独引用的方式只能更换release配置,debug需要同步更换的话可以在debug中引用release的配置,如:

buildTypes {
    debug {
        // 指定 debug 使用release的签名
        signingConfig release.signingConfig
    }
    release {
        ...
    }
}

如果需要在不同风味纬度上也可以指定不同签名,那么需要动态替换,处理方式如下:

android{
        ...
        signingConfigs{
                sign1{...}
                sign2{...}
        }
        buildTypes {
                release {
                      ...
                      //默认不配置signingConfig 
                      //signingConfig signingConfigs.sign1
                }
                debug{
                     ...
                     signingConfig release.signingConfig 
                 }
           }
    android.applicationVariants.all {
          variant ->
              variant.outputs.all { output ->
                  if (variant.flavorName.toString().startsWith("coffee")) {
                      ...
                      variant.mergedFlavor.setSigningConfig signingConfigs.sign1
                  }else{
                      ...
                      variant.mergedFlavor.setSigningConfig signingConfigs.sign2
                  }
                  ...
              }
        }
}

applicationVariants.all是所有配置都会执行的代码,可以在执行过程中根据各个风味的不同对默认配置进行覆盖,而该代码内同样也可以覆盖release的signingConfig信息。

7、资源配置

多渠道环境下不同的渠道可能需要不同的资源(java或者res、assets等等不一样),这时候可以通过sourceSets或者在module的src下分目录的形式进行区分。

1、sourceSets配置方式

...
productFlavors{
        coffee{
            versionCode 11
            versionName "11.0.0"
            buildConfigField "Integer", "UI_VERSION", "1"
        }

        tea{
            versionCode 20
            versionName "20.0.1"
            buildConfigField "Integer", "UI_VERSION", "2"
        }
    }

    sourceSets {
        main {          //默认配置项
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            resources.srcDirs = ['src/main/resources']
            aidl.srcDirs = ['src/main/aidl']
            renderscript.srcDirs = ['src/renders']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
            jniLibs.srcDir 'src/main/jniLibs'
        }
        coffee.res.srcDirs = ['src/main/res-coffee'] //渠道差异配置
        tea.res.srcDirs = ['src/main/res-tea']    //渠道差异配置
    }
...
需要在main文件夹下新增res-coffee和res-tea目录,目录内结构跟main中res结构一致,这样配置之后相同资源会优先选择渠道内的

2、根据渠道名称构建渠道资源目录

可以直接在src目录下根据产品风味名称建立对应的资源目录,其中包含java以及res等部分,然后在各自的资源目录中处理不同风味之间的差异。

注意:
1、main中的Java文件不能和各个风味中的Java同名(包名类名一样)
2、main中res存在和其他风味中的res名称相同时会在编译是被各风味下的res覆盖
3、各风味中不存在的res则会使用main的,所以res资源必须在风味或者main中至少有一个

比如针对coffee和tea两种渠道,可以使用以下目录结构实现资源差异化

|--src
	|--main
		|--java
		|--res
		 --AndroidManifest.xml
	|--coffee
		|--java
		|--res
	|--tea
		|--java
		|--res

在多渠道的环境下,main就是一个公共部分,其他风味都可以共享main中的资源,但是对于java资源,它不存在覆盖的情况,因此可以使用java的extends或者implements等方式进行扩展即可。

res的适配相对来说更加简单,风味资源需要覆盖main资源时只需要在相同的资源目录下新增相同名称的资源即可;如果不需要覆盖则可以随意定义

风味纬度flavorDimensions

flavorDimensions字面意思叫“风味纬度”,需要结合productFlavors一起使用,比如常见的flavorDimensions "default"表示默认一维的维度声明。

android {
   flavorDimensions	"default"
  productFlavors {
	   coffee{
	         ...
	   }
	   tea{
	         ...
	   }
	   ...
      }
      ...
}

一维相当于在productFlavors中一个风味就一个配置,比如示例中coffee、tea都是单独的味道,可以形象的理解为一个一维坐标上的2个点。

---------coffee------------tea------------->

当flavorDimensions声明了多维时,productFlavors需要通过dimension来描述组合的方式,并且这些组合中需要全部包含flavorDimensions的所有声明。比如在coffee和tea之外新增one和two两种可以混合的口味。

android {
   flavorDimensions	"taste","style"
  productFlavors {
	   coffee{
	          ...
			  dimension "taste"
	   }
	   tea{
	          ...
			  dimension "taste"
	   }
	   one{
			  dimension "style"
			}
		 two{
			  dimension "style"
			}
      }
      ...
}

这里定义了taste和style两种纬度,coffe和tea关联taste纬度,而one和two关联style纬度,这样最终就可以组成coffeeOne、coffeeTwo、teaOne、teaTwo这4个基本口味,加上每种有默认的debug和release两种类型,那么就有8中口味(最终可以有8个产物)。这里的二维就相当于一个二维坐标系上的点的组合。

  style	
	^
	|
	|
	one    coffeeOne    teaOne
	|
	|
	two    coffeeTwo    teaTwo
	|
	|
	----------coffee------tea------->tast

其他三维四维以此类推

多渠道的依赖配置

针对不同的渠道,使用不同的依赖,可以在dependencies中使用Flavor名+Compile(或者api、implementation)来指定Flavor对应的所需依赖

dependencies {
    //公共依赖
    compileOnly files('libs/gson.jar')
    ...
    implementation 'androidx.appcompat:appcompat:1.4.2'
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
    //差异化依赖
    coffeeApi(name: 'libA', ext: 'aar')
    teaApi(name: 'libB', ext: 'aar')
    teaImplementation project(':testPro')
}

也可以使用applicationVariants.all进行动态配置依赖关系(尤其是众多渠道中就一两个依赖不一样,直接配置dependencies很麻烦)

ApplicationVariant描述了应用变量属性,其中output方法可以获取到输出相关的变量信息,可以针对这些信息进行相应的打包配置。

android{
    //其他配置...

    flavorDimensions "default"
    productFlavors {
           //产品风味
    }

    android.applicationVariants.all { variant ->
        variant.outputs.all {
            if (variant.flavorName.toString().startsWith("coffee")) {
                dependencies.add("${variant.flavorName}CompileOnly", fileTree(include: ['*.jar'], dir: 'libs1'))
                dependencies.add("${variant.flavorName}Implementation", fileTree(include: ['*.jar'], dir: 'libs2'))
            }else{
                dependencies.add("${variant.flavorName}CompileOnly", fileTree(include: ['*.jar'], dir: 'libs_framework'))
            }
			//配置打包的apk名称(如按风味配置输出文件)
            outputFileName = "app_${variant.productFlavors[0].name}_${productFlavors[0].versionName}.apk"
        }
    }
   //其他配置...
}

注意:如果需要根据不同的渠道名称组合出apk名称,可以直接在productFlavors中根据下标获取,如
outputFileName = "${variant.productFlavors[0].name}_${variant.productFlavors[1].name}_${productFlavors[0].versionName}.apk"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值