【Android】使用Gradle进行配置

【Android】使用Gradle进行配置(记录)

摘要

概念介绍

直接推荐几篇文章入门。

深入理解Android的Gradle

Gradle 用户手册(含部分翻译)

ADT已经不在继续更新了。自然而然的,Android Studio成了新贵。虽说还是有一些别的IDE和别的方式去编写和构建Android,但由于谷歌的维护和升级,AS成了绝大多数Android开发者的选择。对于小团队来说(本人就是在小小小团队),AS + GRADLE 成了现阶段主流。

这是记录,所以概念型的东西就不做介绍了。

需求

由于团队本身有好多服务器(*开发,灰度,线上*),再加上有很多三方的引用(如:我们团队IM引用了环信),另外还有一些日志系统的调控等,再加上长时间不同人员对代码开发,导致这些配置非常混乱,散落于不同的类,还有一些在AndroidManifies.xml等文件中,所以导致导报经常会出现一些莫名其妙的错误。

需求:实现 > 能合理地配置这些,并使用配置文件进行统一管理 >的需求。

目标如下:

  1. 只需更改一处(一行)就能实现不同需求打包的切换!(最重要)
  2. 简单
  3. 不影响Studio相关联的gradle文件(build.gradle一旦改变,就会导致重新编译),但又可以及时更改相关配置。

配置工程

配置BuildConfig

BuildConfig 这个类是工程自动构建生成的。可以具体看一下这个类。

/**
 * Automatically generated file. DO NOT MODIFY
 */
package ****;

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "**.builddemo";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
}

注释说:这个类是自动生成的,别动,动了也没什么用~~~

然后可以看到,这里DEBUG,APPLICATION_ID,BUILD_TYPE,FLAVOR,VERSION_CODE,VERSION_NAME这些常量,这些常量有的是我们在AndroidManifest.xml中配置的,有些是我们在module下的build.grade中配置的(当然,这些其实全部可以在build.gradle中进行配置)

就像这个样子:

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "**.builddemo"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-	rules.pro'
        }
    }

    productFlavors {
    }

}

由于工程中需要versionCode参数,所以BuildConfig便提供了很大的便利。

Tip: defaultConfig{} 这个闭包就是配置工程参数的,也就是直接对应BuildConfig

既然如此,如果能给BuildConfig配置自定义的参数,那么需求就解决了一半了!

显然,Android团队也肯定会留下一些自定义的API,保证大家能 “ 随便乱搞”。

下面是自定义API写法和使用

defaultConfig {
  
       ...
  
		// 自定义的方法就是 buildConfigField ,这种是groovy写法
        // 三个参数分别是 type (类型) , name (命名) , value(值)
        buildConfigField 'int' , 'SEVER_CONFIG' , "1"
  		
  		// 当然写成这种更容易看懂,这种写法更像java。
  		// 三个参数分别是 type (类型) , name (命名) , value(值)
  		buildConfigField("int" , "SEVER_CONFIG" , "1")
  
    }

重新构建后,可以看到

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "**.builddemo";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
  // Fields from default config.
  public static final int SEVER_CONFIG = 1;
}

可以看到int SEVER_CONFIG = 1; 正好对应 'int' , 'SEVER_CONFIG' , "1"

注意, “1” 对应的是 1 , ’ “1” ‘ 对应的是 “1”

是不是很简单。

简单看一下原理吧。

原来是本身构建的时候,android会将闭包转化成相关类,并调用方法。可以看一下buildConfigField这个方法

// -- DSL Methods. TODO remove once the instantiator does what I expect it to do.

    /**
     * Adds a new field to the generated BuildConfig class.
     *
     * The field is generated as:
     *
     * <type> <name> = <value>;
     *
     * This means each of these must have valid Java content. If the type is a String, then the
     * value should include quotes.
     *
     * @param type the type of the field
     * @param name the name of the field
     * @param value the value of the field
     */
    public void buildConfigField(
            @NonNull String type,
            @NonNull String name,
            @NonNull String value) {
        ClassField alreadyPresent = getBuildConfigFields().get(name);
        if (alreadyPresent != null) {
            String flavorName = getName();
            if (BuilderConstants.MAIN.equals(flavorName)) {
                logger.info(
                        "DefaultConfig: buildConfigField '{}' value is being replaced: {} -> {}",
                        name, alreadyPresent.getValue(), value);
            } else {
                logger.info(
                        "ProductFlavor({}): buildConfigField '{}' "
                                + "value is being replaced: {} -> {}",
                        flavorName, name, alreadyPresent.getValue(), value);
            }
        }
        addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
    }

这是ProductFlavor这个类里的方法,不详细说了,大家其实直接看注释就ok了。

解决AndroidManifest.xmlMeta-data

AndroidManifest.xmlMeta-data出来说,其实别的字段也是一样的。

还是build.gradle中的配置

defaultConfig {
    applicationId "zhulonglong.builddemo"
    minSdkVersion 15
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"

    buildConfigField 'int' , 'SEVER_CONFIG' , "1"
    manifestPlaceholders = [ CHANNEL_VALUE : "QQ" ]
}

对应的AndroidManifest.xmlMeta-data数据是

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

       ...
        <meta-data android:name="CHANNEL"
                   android:value="${CHANNEL_VALUE}"/>
</application>

${}是替代符,在defaultConfigmanifestPlaceholders便是对应的AndroidManifest.xml配置文件。

合并,新建config.gradle统一管理所有的配置

创建一个config.gradle的好处:

  1. 无缝衔接gradle构建系统
  2. android studio 方便查看更改,且不会引起重新构建(IDE 自动识别的都是build.gradle文件,对于别的gradle文件不会在乎)

我创建的config.grade是在项目根录下的。


def DEV = 0
def ONLINE = 2

ext{
    SEVER_CONFIG = ONLINE
    CHANNEL_DEV = "CHANNEL_DEV"
    CHANNEL_ONLINE = "CHANNEL_ONLINE"

    if (SEVER_CONFIG == DEV){
        CHANNEL_NAME = CHANNEL_DEV
    }else {
        CHANNEL_NAME = CHANNEL_ONLINE
    }

}

ext保证了别的gradle能顺利使用这个属性

在需要的build.gradle中导入config.gradle

apply from: rootProject.getRootDir().getAbsolutePath() + "/config.gradle"

println "Project Name " + getName()
println "Project Path " + getPath()

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "**.builddemo"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

        buildConfigField 'int' , 'SEVER_CONFIG' , "${SEVER_CONFIG}"

        manifestPlaceholders = [ CHANNEL_VALUE : "${CHANNEL_NAME}" ]

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    productFlavors {


    }

}

然后就可以直接引用这个属性了。

如:

buildConfigField 'int' , 'SEVER_CONFIG' , "${SEVER_CONFIG}"

SEVER_CONFIG就是在config.gradle中定义的。

到此为止,目的达到了。

所有的配置都在文件config.gradle中进行更改。当然,还需要在里面定义一个总开关,将具体参数配置分块。

在代码中可以直接使用BuildConfig进行相关使用。

当然,更改config.gradle不会进行重新构建,是不是棒棒哒!!!


comments powered by Disqus