登录

去注册

登录

注册

去登录

注册

每日一问 今天聊一下Gradle 相关,BuildConfig这个类是如何生成的?

xiaoyang   2019-08-20   收藏

就找个细节考察下 Gradle 相关知识吧。

每个项目中,gradle 都会帮助我们生成一个 BuildConfig,那么:

  1. 这个类有何用处?
  2. 是在项目的编译期间,那个环节、如何生成的?

欢迎了解多少说多少,不了解学了过来总结一下。

9

BuildConfig的用处:

@陈熔 同学已经讲的很详细了。

BuildConfig是怎样生成的?

为了能看到Gradle的源码,首先要添加下依赖(版本可以跟当前项目中的gradle版本):

implementation 'com.android.tools.build:gradle:3.4.2'

想一下,既然它是自动生成的,按常理,在刚刚引用的gradle库里面,肯定有BuildConfig之类的字眼吧?全局搜一下:
会发现在一个叫BuildConfigGenerator的类里面有这么一句:

public static final String BUILD_CONFIG_NAME = "BuildConfig.java";

哈哈哈,CTRL点开这个常量,可以看到一个叫generate的方法用到了它(看名字就知道是生成的意思了),看下精简后的代码:

    public void generate() throws IOException {
        // 创建BuildConfig.java的File对象
        File buildConfigJava = new File(pkgFolder, BUILD_CONFIG_NAME);
        try {
            // 根据刚刚创建的File对象,创建JavaWriter对象,
            // 这个对象就是用来生成java源码文件的
            JavaWriter writer = closer.register(buildConfigJava);
            // 写入BuildConfig顶部的文档描述
            writer.emitJavadoc("Automatically generated file. DO NOT MODIFY")
                    // 写入包名
                    .emitPackage(mBuildConfigPackageName)
                    // 定义BuildConfig类
                    .beginType("BuildConfig", "class", PUBLIC_FINAL);

            // 遍历写入在build.gradle中定义的常量
            for (ClassField field : mFields) {
                emitClassField(writer, field);
            }
            // 完成
            writer.endType();
        } finally {
            closer.close();
        }
    }

emmm,没错了,BuildConfig这个类以及它里面的内容,就是在这个方法中利用square的开源项目 javapoet - JavaWriter 来生成的。

BuildConfig是在哪个环节中生成的?

我们可以用顺瓜模藤来找到调用generate的源头:

GenerateBuildConfig.generate() ->
TaskManager.createBuildConfigTask() -> 
(ApplicationTaskManager, LibraryTaskManager).createTasksForVariantScope -> 
VariantManager.createAndroidTasks() ->
BasePlugin.createAndroidTasks() -> 
BasePlugin.createTasks() -> 
BasePlugin.basePluginApply() -> 
BasePlugin.apply() ->

可以看到调用链的源头就是BasePluginapply方法,但要搞清楚生成BuildConfig的任务执行时机,还是要看回ApplicationTaskManagerLibraryTaskManagercreateTasksForVariantScope方法(任务太多了,精简掉了一些不太熟悉的任务):

    public void createTasksForVariantScope(@NonNull final VariantScope variantScope) {
        createAnchorTasks(variantScope);
        createCheckManifestTask(variantScope);

        // Add a task to publish the applicationId.
        createApplicationIdWriterTask(variantScope);

        // Add a task to process the manifest(s)
        createMergeApkManifestsTask(variantScope);

        // Add a task to create the res values
        createGenerateResValuesTask(variantScope);

        // Add a task to merge the resource folders
        createMergeResourcesTask(variantScope);

        // Add tasks to compile shader
        createShaderTask(variantScope);

        // Add a task to merge the asset folders
        createMergeAssetsTask(variantScope);

        // Add a task to create the BuildConfig class
        createBuildConfigTask(variantScope);

        createAidlTask(variantScope);

        // Add a compile task
        createCompileTask(variantScope);

        // Create the lint tasks, if enabled
        createLintTasks(variantScope);
    }

可以看到是在MergeResources和MergeAssets之后才生成的,完成之后,就开始生成Aidl文件的java代码了。

回复
陈小缘 : @陈小缘 

参考文章:https://www.jianshu.com/p/238b78368ba6

2019-08-20 回复
6
1、BuildConfig的用处

程序编译成功后,会在每一个Module下的build\generated\source\buildConfig\debug(release)\包名下生成一个BuildConfig文件。BuildConfig内容如下:

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.example.myapplication";
  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";
}

从内容中可以看出,BuildConfig是根据Module下的build.gradle生成的。
其中最常用的是BuildConfig.DEBUG,判断当前是否处于debug模式,来控制日志的输出。这个值会根据开发者的Build类型自动设定,不需要手动设置。
除此之外,还可以自定义添加BuildConfig里的常量,比如设置开发环境和正式环境下的网络请求地址和其他常量。
在Module下的build.gradle文件中的defaultConfig和buildTypes中添加 buildConfigField "String", "BASE_STRING", "\"string content\"" 即可

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 15
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        buildConfigField "String", "BASE_STRING", "\"string content\""
        buildConfigField "boolean", "BASE_BOOLEAN", "false"
        buildConfigField "int", "BASE_INT", "1"
    }

    buildTypes {
        release {
            buildConfigField "String", "BASE_URL", "\"http://www.google.com.hk\""
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }

        debug {
            buildConfigField "String", "BASE_URL", "\"http://www.baidu.com\""
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

区别在于buildTypes会根据Build类型引用不同的值,defaultConfig是通用的值。

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.example.myapplication";
  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 build type: debug
  public static final String BASE_URL = "http://www.baidu.com";
  // Fields from default config.
  public static final boolean BASE_BOOLEAN = false;
  public static final int BASE_INT = 1;
  public static final String BASE_STRING = "string content";
}

buildConfigField "String", "BASE_STRING", "\"string content\"" 这句代码中三个参数分别是数据类型,常量名,常量值。
这里要注意的是BuildConfig是通过String读取数据的,所以当常量值数据类型为String时,需要在双引号里面在添加一个双引号。
常量值的可以直接写在build.gradle中,不过更推荐定义在gradle.properties中,然后在build.gradle中引用即可。
在gradle.properties中定义

BASE_STRING = "string content"
BASE_BOOLEAN = false
BASE_INT = 1

然后在build.gradle中引用时

buildConfigField "String", "BASE_STRING", BASE_STRING
buildConfigField "boolean", "BASE_BOOLEAN", BASE_BOOLEAN
buildConfigField "int", "BASE_INT", BASE_INT

因为gradle.properties里定义的value默认都是String,所以在定义build.gradle中可以直接使用

2、BuildConfig的生成

至于BuildConfig在项目的编译期间,那个环节、如何生成的,暂时没有找到好的答案。

回复
鸿洋 : @陈熔 

感谢分享~

2019-08-19 回复
1

1.用处:可以用来获取APP的编译模式,包名,编译类型,版本号,版本名
2.是在项目的编译期间AS自动生成的...

回复

删除留言

确认删除留言,会导致相关评论丢失?

取消 确定