gradle 的配置(多渠道打包,改包名,测试环境vs正式环境处理方式,配置宏)

1 前言

gradle 的配置不是很简单因为很多东西其实不知道有,一般都是要用的时候才查,才去配所以在此纪录下。其中包括:
多渠道打包,
改包名,
过滤不要的渠道或者环境,
测试环境和正式环境设置不同域名

2 基础概念

defaultConfig :默认配置
buildTypes : 编译类型,默认的就是debug,release
productFlavors:渠道
buildConfigField : 配置宏,可以在gradle里配置一个值让java 代码中访问到,其中 defaultConfig buildTypes productFlavors 都可以配置buildConfigField。

举例:
比如现在要做:
1. 多渠道打包( 豌豆荚,360手机助手,应用包,小米市场, 这些渠道对应 productFlavors。
2. 比如现在给电信做项目,福建版本,浙江版本,上海版本,北京版本,这些也是对应的 productFlavors。
3. 那么每种渠道对应常见的编译类型 调试包(debug ),发布包(release) 对应的就是buildTypes 的概念。当然我们也可以新建另一个名字的编译类型其实就是另一个名字的release而已,不建议在buildTypes里加类型,要加加到productFlavors里,比较符合逻辑
4. defaultConfig 里常见的选项有
applicationId(对应包名),
minSdkVersion (最小版本)
targetSdkVersion (目标版本,6.0后与权限设置相关)
versionCode 1 (版本号,不建议写这个参数,更推荐用Mainfest.xml里写)
versionName “1.0”(版本名,不建议写这个参数,更推荐用Mainfest.xml里写)

3 多渠道打包

这个没啥好说的,基本都是以友盟为例,写gradle的脚本,去替换Mainfest.xml 里的标签达到效果:

3.1 AndroidManifest.xml 配置

<meta-data
     android:name="${UMENG_CHANNEL_VALUE}"
     android:value="UMENG_CHANNEL" />
    
    
  • 1
  • 2
  • 3

3.2 gradle配置

productFlavors {
        xiaomi {}
        yingyongbao {}
        wandoujia {}
        baidu {}
        c360 {}
        uc {}

        productFlavors.all { flavor ->
            flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }

    }

//自定义生成的apk的名称
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                def fileName = "your_name" + "_${variant.productFlavors[<span class="hljs-number">0</span>].name}</span>_v<span class="hljs-subst">${getVersionNameFromManifest()}_c${getVersionCodeFromManifest()}</span>_<span class="hljs-subst">${variant.buildType.name}.apk"
                output.outputFile = new File(outputFile.parent, fileName)
            }
        }
        //过滤掉unaligned的包
        variant.assemble.doLast {
            variant.outputs.each { output ->
                println "-----------------------------------------"
                println "aligned " + output.outputFile
                println "unaligned " + output.packageApplication.outputFile
                File unaligned = output.packageApplication.outputFile;
                File aligned = output.outputFile
                if (!unaligned.getName().equalsIgnoreCase(aligned.getName())) {
                    println "deleting " + unaligned.getName()
                    unaligned.delete()
                }
            }
        }
    }
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

3.3 gradle 中获取 AndroidManifest.xml 中的版本号,与版本名

//gradle2.2以前-------------------
//从androidManifest.xml中获取版本号
//def getVersionNameFromManifest() {
//    def manifestParser = new com.android.builder.core.DefaultManifestParser()
//    return manifestParser.getVersionName(android.sourceSets.main.manifest.srcFile)
//}

//从androidManifest.xml中获取Code
//def getVersionCodeFromManifest() {
//    def manifestParser = new com.android.builder.core.DefaultManifestParser()
//    return manifestParser.getVersionCode(android.sourceSets.main.manifest.srcFile)
//}

//gralde2.2+ ----------------------------
def getVersionNameFromManifest() {
    def manifestParser = new com.android.builder.core.DefaultManifestParser(android.sourceSets.main.manifest.srcFile)
    return manifestParser.getVersionName()
}
//从androidManifest.xml中获取Code
def getVersionCodeFromManifest() {
    def manifestParser = new com.android.builder.core.DefaultManifestParser(android.sourceSets.main.manifest.srcFile)
    return manifestParser.getVersionCode()
}
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3.4 修改包名,和删除指定包

参见3.2中代码

3.5 过滤器

比如过滤掉release 的版本,只打debug的版本。过滤器和applicationVariants.all 是在同一层 都在android{}里

  //编译过滤器
    variantFilter { variant ->
        def buildType = variant.buildType.name
        def flavorName = variant.getFlavors().get(0).name// 根据构建类型,自动过滤渠道

  //过滤掉类型
   if (buildType.equals('release')) {
            println "======================="
            println "variantFilter " + flavorName + " " + buildType
            variant.setIgnore(true)
        }
    }
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

想要看println日志信息双击Tasks–build–assembleRelease在run窗口即可看到

4 测试环境和正式环境的不同配置

一般正常的开发服务器都有份测试环境,正式环境,严格的公司还分预发布环境。每种环境的服务端不一样,很典型的体现就是接口请求的域名就不同。之前查材料的时候 看到有些人把正式环境的域名是配在release里 测试环境的域名配在debug里,这是不对的。概念混淆了,任何一个环境都是要能打出debug包调试,打出release包发布出去用的。典型的就是debug包才能看一些关键log 和按步调试。

4.1 buildConfigField 配置一个宏,代码中可以访问

buildConfigField 可以配置常见的数据类型,比如String int boolean 供java代码调用,编译后会生成一个文件BuildConfig 直接可以引用其中的配置宏,如下

public final class BuildConfig {
  public static final boolean DEBUG = false;
  public static final String APPLICATION_ID = "com.xxx.xxx";
  public static final String BUILD_TYPE = "release";
  public static final String FLAVOR = "xiaomi";
  public static final int VERSION_CODE = 6;
  public static final String VERSION_NAME = "1.0.6";
  // Fields from default config.
  public static final String DOMAIN_USER = "http://user.xxxx.com";
  public static final boolean IS_JUST_TEST = false;

}
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4.2 配置宏的优先级

defaultConfig, buildTypes, productFlavors 都可以配置buildConfigField。
优先级buildTypes > productFlavors > defaultConfig 就是说如果三个地方都配置同一个宏,那么最后保留的是优先级高的那个。优先级高的宏 覆盖优先级低的宏。 如果不覆盖就是并排展示

展示例子

BuildConfig:
// Fields from build type: debug


// Fields from product flavor: onlytest


// Fields from default config.
    
    
  • 1
  • 2
  • 3
  • 4

4.3 测试环境和正式环境的处理方式

我是这么处理的,把测试环境当成一个渠道,如果有预发布环境当成另一个渠道 都写在 productFlavors 中,把其它渠道其实都是发布的时候用的都是正事环境写在 defaultConfig中,如果是类似电信的分地点作为渠道的,那么也是写在 productFlavors 中比如:

 defaultConfig {


        buildConfigField "boolean", "IS_JUST_TEST", "false"
buildConfigField "String", "DOMAIN_NAME", "\"yourDomain\""
        multiDexEnabled true
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        applicationId "com.yourname"


    }

productFlavors {

        onlytest {


            buildConfigField "String", "DOMAIN_NAME", "\"yourTestDomain\""

            buildConfigField "boolean", "IS_JUST_TEST", "true"

        }
        loop {}
        xiaomi {}
        yingyongbao {}
        wandoujia {}
        baidu {}
        c360 {}
        uc {}

        productFlavors.all { flavor ->
            flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }

    }
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

java 代码引用域名 BuildConfig.DOMAIN_NAME
java 代码需要判断是不是测试环境的用 BuildConfig.IS_JUST_TEST 当然用渠道名字 BuildConfig.FLAVOR 也可以 不过为不喜欢这个

补充:针对4.1小节buildConfigField 配置一个宏,代码中可以访问:具体给个例子如下


在build.gradle文件配置如下:
   buildTypes {
    debug {
        resValue "string", "Test_Password", '"666666"'//生成资源文件R.string.Test_Password
        buildConfigField "boolean", "isInsertPassword", "true"// 数据类型, 变量名,变量值
        signingConfig signingConfigs.konying
    }
    release {
        buildConfigField "boolean", "isInsertPassword", "false"//
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.konying
    }
}

系统自动生成的BuildConfig文件在app\build\generated\source\buildConfig下

    public final class BuildConfig {
      public static final boolean DEBUG = Boolean.parseBoolean("true");
      public static final String APPLICATION_ID = "com.konying.persionsys2";
      public static final String BUILD_TYPE = "debug";
      public static final String FLAVOR = "icon";
      public static final int VERSION_CODE = 1;
      public static final String VERSION_NAME = "V1.6.0.1";
      // Fields from build type: debug
      public static final boolean isInsertPassword = true;//debug版本
    }
    public final class BuildConfig {
      public static final boolean DEBUG = false;
      public static final String APPLICATION_ID = "com.konying.persionsys2";
      public static final String BUILD_TYPE = "release";
      public static final String FLAVOR = "icon";
      public static final int VERSION_CODE = 1;
      public static final String VERSION_NAME = "V1.6.0.1";
      // Fields from build type: release
      public static final boolean isInsertPassword = false;//release版本
    }

在LoginActivity中可以访问

      //debug 版本添加默认密码,其他版本不添加
        if (BuildConfig.isInsertPassword) {
            mPasswordEditText.setText(R.string.Test_Password);
        }

1 前言

gradle 的配置不是很简单因为很多东西其实不知道有,一般都是要用的时候才查,才去配所以在此纪录下。其中包括:
多渠道打包,
改包名,
过滤不要的渠道或者环境,
测试环境和正式环境设置不同域名

2 基础概念

defaultConfig :默认配置
buildTypes : 编译类型,默认的就是debug,release
productFlavors:渠道
buildConfigField : 配置宏,可以在gradle里配置一个值让java 代码中访问到,其中 defaultConfig buildTypes productFlavors 都可以配置buildConfigField。

举例:
比如现在要做:
1. 多渠道打包( 豌豆荚,360手机助手,应用包,小米市场, 这些渠道对应 productFlavors。
2. 比如现在给电信做项目,福建版本,浙江版本,上海版本,北京版本,这些也是对应的 productFlavors。
3. 那么每种渠道对应常见的编译类型 调试包(debug ),发布包(release) 对应的就是buildTypes 的概念。当然我们也可以新建另一个名字的编译类型其实就是另一个名字的release而已,不建议在buildTypes里加类型,要加加到productFlavors里,比较符合逻辑
4. defaultConfig 里常见的选项有
applicationId(对应包名),
minSdkVersion (最小版本)
targetSdkVersion (目标版本,6.0后与权限设置相关)
versionCode 1 (版本号,不建议写这个参数,更推荐用Mainfest.xml里写)
versionName “1.0”(版本名,不建议写这个参数,更推荐用Mainfest.xml里写)

3 多渠道打包

这个没啥好说的,基本都是以友盟为例,写gradle的脚本,去替换Mainfest.xml 里的标签达到效果:

3.1 AndroidManifest.xml 配置

<meta-data
     android:name="${UMENG_CHANNEL_VALUE}"
     android:value="UMENG_CHANNEL" />
  
  
  • 1
  • 2
  • 3

3.2 gradle配置

productFlavors {
        xiaomi {}
        yingyongbao {}
        wandoujia {}
        baidu {}
        c360 {}
        uc {}

        productFlavors.all { flavor ->
            flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }

    }

//自定义生成的apk的名称
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                def fileName = "your_name" + "_${variant.productFlavors[<span class="hljs-number">0</span>].name}</span>_v<span class="hljs-subst">${getVersionNameFromManifest()}_c${getVersionCodeFromManifest()}</span>_<span class="hljs-subst">${variant.buildType.name}.apk"
                output.outputFile = new File(outputFile.parent, fileName)
            }
        }
        //过滤掉unaligned的包
        variant.assemble.doLast {
            variant.outputs.each { output ->
                println "-----------------------------------------"
                println "aligned " + output.outputFile
                println "unaligned " + output.packageApplication.outputFile
                File unaligned = output.packageApplication.outputFile;
                File aligned = output.outputFile
                if (!unaligned.getName().equalsIgnoreCase(aligned.getName())) {
                    println "deleting " + unaligned.getName()
                    unaligned.delete()
                }
            }
        }
    }
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

3.3 gradle 中获取 AndroidManifest.xml 中的版本号,与版本名

//gradle2.2以前-------------------
//从androidManifest.xml中获取版本号
//def getVersionNameFromManifest() {
//    def manifestParser = new com.android.builder.core.DefaultManifestParser()
//    return manifestParser.getVersionName(android.sourceSets.main.manifest.srcFile)
//}

//从androidManifest.xml中获取Code
//def getVersionCodeFromManifest() {
//    def manifestParser = new com.android.builder.core.DefaultManifestParser()
//    return manifestParser.getVersionCode(android.sourceSets.main.manifest.srcFile)
//}

//gralde2.2+ ----------------------------
def getVersionNameFromManifest() {
    def manifestParser = new com.android.builder.core.DefaultManifestParser(android.sourceSets.main.manifest.srcFile)
    return manifestParser.getVersionName()
}
//从androidManifest.xml中获取Code
def getVersionCodeFromManifest() {
    def manifestParser = new com.android.builder.core.DefaultManifestParser(android.sourceSets.main.manifest.srcFile)
    return manifestParser.getVersionCode()
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3.4 修改包名,和删除指定包

参见3.2中代码

3.5 过滤器

比如过滤掉release 的版本,只打debug的版本。过滤器和applicationVariants.all 是在同一层 都在android{}里

  //编译过滤器
    variantFilter { variant ->
        def buildType = variant.buildType.name
        def flavorName = variant.getFlavors().get(0).name// 根据构建类型,自动过滤渠道

  //过滤掉类型
   if (buildType.equals('release')) {
            println "======================="
            println "variantFilter " + flavorName + " " + buildType
            variant.setIgnore(true)
        }
    }
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

想要看println日志信息双击Tasks–build–assembleRelease在run窗口即可看到

4 测试环境和正式环境的不同配置

一般正常的开发服务器都有份测试环境,正式环境,严格的公司还分预发布环境。每种环境的服务端不一样,很典型的体现就是接口请求的域名就不同。之前查材料的时候 看到有些人把正式环境的域名是配在release里 测试环境的域名配在debug里,这是不对的。概念混淆了,任何一个环境都是要能打出debug包调试,打出release包发布出去用的。典型的就是debug包才能看一些关键log 和按步调试。

4.1 buildConfigField 配置一个宏,代码中可以访问

buildConfigField 可以配置常见的数据类型,比如String int boolean 供java代码调用,编译后会生成一个文件BuildConfig 直接可以引用其中的配置宏,如下

public final class BuildConfig {
  public static final boolean DEBUG = false;
  public static final String APPLICATION_ID = "com.xxx.xxx";
  public static final String BUILD_TYPE = "release";
  public static final String FLAVOR = "xiaomi";
  public static final int VERSION_CODE = 6;
  public static final String VERSION_NAME = "1.0.6";
  // Fields from default config.
  public static final String DOMAIN_USER = "http://user.xxxx.com";
  public static final boolean IS_JUST_TEST = false;

}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4.2 配置宏的优先级

defaultConfig, buildTypes, productFlavors 都可以配置buildConfigField。
优先级buildTypes > productFlavors > defaultConfig 就是说如果三个地方都配置同一个宏,那么最后保留的是优先级高的那个。优先级高的宏 覆盖优先级低的宏。 如果不覆盖就是并排展示

展示例子

BuildConfig:
// Fields from build type: debug


// Fields from product flavor: onlytest


// Fields from default config.
  
  
  • 1
  • 2
  • 3
  • 4

4.3 测试环境和正式环境的处理方式

我是这么处理的,把测试环境当成一个渠道,如果有预发布环境当成另一个渠道 都写在 productFlavors 中,把其它渠道其实都是发布的时候用的都是正事环境写在 defaultConfig中,如果是类似电信的分地点作为渠道的,那么也是写在 productFlavors 中比如:

 defaultConfig {


        buildConfigField "boolean", "IS_JUST_TEST", "false"
buildConfigField "String", "DOMAIN_NAME", "\"yourDomain\""
        multiDexEnabled true
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        applicationId "com.yourname"


    }

productFlavors {

        onlytest {


            buildConfigField "String", "DOMAIN_NAME", "\"yourTestDomain\""

            buildConfigField "boolean", "IS_JUST_TEST", "true"

        }
        loop {}
        xiaomi {}
        yingyongbao {}
        wandoujia {}
        baidu {}
        c360 {}
        uc {}

        productFlavors.all { flavor ->
            flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }

    }
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

java 代码引用域名 BuildConfig.DOMAIN_NAME
java 代码需要判断是不是测试环境的用 BuildConfig.IS_JUST_TEST 当然用渠道名字 BuildConfig.FLAVOR 也可以 不过为不喜欢这个

补充:针对4.1小节buildConfigField 配置一个宏,代码中可以访问:具体给个例子如下


在build.gradle文件配置如下:
   buildTypes {
    debug {
        resValue "string", "Test_Password", '"666666"'//生成资源文件R.string.Test_Password
        buildConfigField "boolean", "isInsertPassword", "true"// 数据类型, 变量名,变量值
        signingConfig signingConfigs.konying
    }
    release {
        buildConfigField "boolean", "isInsertPassword", "false"//
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.konying
    }
}

系统自动生成的BuildConfig文件在app\build\generated\source\buildConfig下

    public final class BuildConfig {
      public static final boolean DEBUG = Boolean.parseBoolean("true");
      public static final String APPLICATION_ID = "com.konying.persionsys2";
      public static final String BUILD_TYPE = "debug";
      public static final String FLAVOR = "icon";
      public static final int VERSION_CODE = 1;
      public static final String VERSION_NAME = "V1.6.0.1";
      // Fields from build type: debug
      public static final boolean isInsertPassword = true;//debug版本
    }
    public final class BuildConfig {
      public static final boolean DEBUG = false;
      public static final String APPLICATION_ID = "com.konying.persionsys2";
      public static final String BUILD_TYPE = "release";
      public static final String FLAVOR = "icon";
      public static final int VERSION_CODE = 1;
      public static final String VERSION_NAME = "V1.6.0.1";
      // Fields from build type: release
      public static final boolean isInsertPassword = false;//release版本
    }

在LoginActivity中可以访问

      //debug 版本添加默认密码,其他版本不添加
        if (BuildConfig.isInsertPassword) {
            mPasswordEditText.setText(R.string.Test_Password);
        }

猜你喜欢

转载自blog.csdn.net/hizhangyuping/article/details/80899405