文章目录

    • Task5 checkDebugManifest
      • 1. input/ouput
      • 2. 核心类(CheckManifest)
    • Task6 generateDebugBuildConfig
      • 1. input/output
      • 2. 核心类(GenerateBuildConfig)
    • Task7 prepareLintJar
      • 1. input/ouput
      • 2. 核心类(PrepareLintJar)
    • Task8 mainApkListPersistenceDebug
      • 1. input/output
      • 2. 核心类(MainApkListPersistence)

Task5 checkDebugManifest

1. input/ouput

taskName:checkDebugManifest=========================================================output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/check-manifest/debug

2. 核心类(CheckManifest)

@TaskActionvoid check() {    if (!isOptional && manifest != null && !manifest.isFile()) {        throw new IllegalArgumentException(                String.format(                        "Main Manifest missing for variant %1$s. Expected path: %2$s",                        getVariantName(), getManifest().getAbsolutePath()));    }}

该任务是校验操作,就是校验清单文件的合法性,不合法则中断任务,抛出异常

配置阶段操作

@Overridepublic void execute(@NonNull CheckManifest checkManifestTask) {//1. 设置任务实现类    scope.getTaskContainer().setCheckManifestTask(checkManifestTask);//2. 设置相关入参    checkManifestTask.setVariantName(            scope.getVariantData().getVariantConfiguration().getFullName());    checkManifestTask.setOptional(isManifestOptional);    checkManifestTask.manifest =            scope.getVariantData().getVariantConfiguration().getMainManifest();//3. 出参目录配置    checkManifestTask.fakeOutputDir =            new File(                    scope.getGlobalScope().getIntermediatesDir(),                    "check-manifest/" + scope.getVariantConfiguration().getDirName());}

依赖任务:preBuildTask

Task6 generateDebugBuildConfig

BuildConfig.java想必大家非常熟悉,该任务就是生成它

1. input/output

taskName:generateDebugBuildConfig=========================================================output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/generated/source/buildConfig/debug========================END==============================

2. 核心类(GenerateBuildConfig)

@TaskActionvoid generate() throws IOException {  //1. 清理操作      // must clear the folder in case the packagename changed, otherwise,      // there'll be two classes.      File destinationDir = getSourceOutputDir();      FileUtils.cleanOutputDir(destinationDir);  //2. 构建一个BuildConfig生成器,用于生成BuildConfig.java      BuildConfigGenerator generator = new BuildConfigGenerator(              getSourceOutputDir(),              getBuildConfigPackageName());      // Hack (see IDEA-100046): We want to avoid reporting "condition is always true"      // from the data flow inspection, so use a non-constant value. However, that defeats      // the purpose of this flag (when not in debug mode, if (BuildConfig.DEBUG && ...) will      // be completely removed by the compiler), so as a hack we do it only for the case      // where debug is true, which is the most likely scenario while the user is looking      // at source code.      //map.put(PH_DEBUG, Boolean.toString(mDebug));  //3. 给生成器对象配置6个固定字段  // DEBUG、APPLICATION_ID、BUILD_TYPE、FLAVOR、VERSION_CODE、VERSION_NAME      generator              .addField(                      "boolean",                      "DEBUG",                      isDebuggable() ? "Boolean.parseBoolean(\"true\")" : "false")              .addField("String", "APPLICATION_ID", '"' + appPackageName.get() + '"')              .addField("String", "BUILD_TYPE", '"' + getBuildTypeName() + '"')              .addField("String", "FLAVOR", '"' + getFlavorName() + '"')              .addField("int", "VERSION_CODE", Integer.toString(getVersionCode()))              .addField(                      "String", "VERSION_NAME", '"' + Strings.nullToEmpty(getVersionName()) + '"')              .addItems(getItems());//4. getItems,则是我们在build.gradle自定义的字段属性值  //5. 生成flavor字段      List<String> flavors = getFlavorNamesWithDimensionNames();      int count = flavors.size();      if (count > 1) {          for (int i = 0; i < count; i += 2) {              generator.addField(                      "String", "FLAVOR_" + flavors.get(i + 1), '"' + flavors.get(i) + '"');          }      }      generator.generate();  }

在build.gralde -> defaultConfig 添加

buildConfigField("String", "name", "\"xiaobaoyihao\"")

执行

./gradlew generateDebugBuildConfig

看下BuildConfig.java结构

/** * Automatically generated file. DO NOT MODIFY */package com.gradle.task.demo;public final class BuildConfig {  public static final boolean DEBUG = Boolean.parseBoolean("true");  public static final String APPLICATION_ID = "com.gradle.task.demo";  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 String name = "xiaobaoyihao";}

可以看到自定义属性字段成功写入到BuildConfig.java类中了;至于类生成具体实现细节是在generate方法中,主要使用了文件输出流配合JavaWriter类操作来协调完成的,有兴趣自行研究

//BuildConfigGenerator.javapublic void generate() throws IOException {    File pkgFolder = getFolderPath();    if (!pkgFolder.isDirectory()) {        if (!pkgFolder.mkdirs()) {            throw new RuntimeException("Failed to create " + pkgFolder.getAbsolutePath());        }    }    File buildConfigJava = new File(pkgFolder, BUILD_CONFIG_NAME);    Closer closer = Closer.create();    try {        FileOutputStream fos = closer.register(new FileOutputStream(buildConfigJava));        OutputStreamWriter out = closer.register(new OutputStreamWriter(fos, Charsets.UTF_8));        JavaWriter writer = closer.register(new JavaWriter(out));//1. 生成文件头注释说明文案        writer.emitJavadoc("Automatically generated file. DO NOT MODIFY")                .emitPackage(mBuildConfigPackageName)                .beginType("BuildConfig", "class", PUBLIC_FINAL);//2. 就是上面提到的6个固定字段属性        for (ClassField field : mFields) {            emitClassField(writer, field);        }//3. 自定义属性字段        for (Object item : mItems) {            if (item instanceof ClassField) {                emitClassField(writer, (ClassField) item);            } else if (item instanceof String) {                writer.emitSingleLineComment((String) item);            }        }        writer.endType();    } catch (Throwable e) {        throw closer.rethrow(e);    } finally {        closer.close();    }}

Task7 prepareLintJar

1. input/ouput

taskName:prepareLintJar=========================================================output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/lint_jar/global/prepareLintJar/lint.jar

2. 核心类(PrepareLintJar)

/** * Task that takes the configuration result, and check that it's correct. * * 

Then copies it in the build folder to (re)publish it. This is not super efficient but because * publishing is done at config time when we don't know yet what lint.jar file we're going to * publish, we have to do this. */public class PrepareLintJar extends DefaultTask {...@TaskActionpublic void prepare() throws IOException { // there could be more than one files if the dependency is on a sub-projects that // publishes its compile dependencies. Rather than query getSingleFile and fail with // a weird message, do a manual check //1. 获取项目中依赖的自定义lint规则jar,此处gradle插件对其做了限制,只能有一个lint.jar超过一个会build失败 Set<File> files = lintChecks.getFiles(); if (files.size() > 1) { throw new RuntimeException( "Found more than one jar in the '" + VariantDependencies.CONFIG_NAME_LINTCHECKS + "' configuration. Only one file is supported. If using a separate Gradle project, make sure compilation dependencies are using compileOnly"); }//2. 如果项目中没有自定义lint规则,则清理文件,否则copyt lint.jar到指定目录 if (files.isEmpty()) { if (outputLintJar.isFile()) { FileUtils.delete(outputLintJar); } } else { FileUtils.mkdirs(outputLintJar.getParentFile()); Files.copy(Iterables.getOnlyElement(files), outputLintJar); }}//ConfigAction.java@Overridepublic void execute(@NonNull PrepareLintJar task) {//指定输出位置 task.outputLintJar = scope.getArtifacts() .appendArtifact(InternalArtifactType.LINT_JAR, task, FN_LINT_JAR); //读取自定义规则文件集合 task.lintChecks = scope.getLocalCustomLintChecks();}}

从上可以看出这个任务就是copy功能,lint.jar不能超过1个,也就是说如果项目中存在混合语言时,如果你要对其进行各自自定义一套check规则(每套规则会生成一个lint.jar),那对不起,执行到这任务直接中断,这应该算Android插件一个缺陷吧,高版本已修复;

关于如何自定义check规则,可以参考如下官网相关链接

https://developer.android.com/studio/write/lint?hl=zh-cn
https://github.com/googlesamples/android-custom-lint-rules.git
http://tools.android.com/tips/lint-custom-rules

Task8 mainApkListPersistenceDebug

1. input/output

taskName:mainApkListPersistenceDebug=========================================================output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/apk_list/debug/mainApkListPersistenceDebug/apk-list.gson

格式化后的apk-list.gson

[  {    "type": "MAIN",    "splits": [],    "versionCode": 1,    "versionName": "1.0",    "enabled": true,    "outputFile": "app-debug.apk",    "fullName": "debug",    "baseName": "debug"  }]

这个任务其实就是生成一个apk信息的gson文件,看下核心入口代码

2. 核心类(MainApkListPersistence)

这个类非常简短,我直接全部贴出来吧,有意思的是它是kotlin写的

/** * Task to persist the {@see OutputScope#apkdatas} which allows downstream tasks to depend * on the {@see InternalArtifactType#APK_LIST} rather than on various complicated data structures. * This also allow to record the choices made during configuration time about what APKs will be * produced and which ones are enabled. */open class MainApkListPersistence : AndroidVariantTask() {    @get:OutputFile    lateinit var outputFile: File        private set    @get:Input    lateinit var apkData : Collection<ApkData>        private set    @TaskAction    fun fullTaskAction() {//1. 清理操作        FileUtils.deleteIfExists(outputFile)        //2. 返回一个apk信息的字符串        val apkDataList = ExistingBuildElements.persistApkList(apkData)//3. 写入文件        FileUtils.createFile(outputFile, apkDataList)    }    class ConfigAction(            val scope: VariantScope) :            TaskConfigAction<MainApkListPersistence> {        override fun getName() = scope.getTaskName("mainApkListPersistence")        override fun getType() = MainApkListPersistence::class.java        override fun execute(task: MainApkListPersistence) {            task.variantName = scope.fullVariantName            task.apkData = scope.outputScope.apkDatas            task.outputFile = scope.artifacts.appendArtifact(                InternalArtifactType.APK_LIST,                task,                SdkConstants.FN_APK_LIST)        }    }}

可以看到关键代码其实是在第二部操作中,我们看看内部如何实现的,

//ExistingBuildElements.kt@JvmStaticfun persistApkList(apkInfos: Collection<ApkInfo>): String {    val gsonBuilder = GsonBuilder()    gsonBuilder.registerTypeHierarchyAdapter(ApkInfo::class.java, ApkInfoAdapter())    val gson = gsonBuilder.create()    return gson.toJson(apkInfos)}//ApkInfoAdapter.ktoverride fun write(out: JsonWriter, value: ApkInfo?) {    if (value == null) {        out.nullValue()        return    }    out.beginObject()    out.name("type").value(value.type.toString())    out.name("splits").beginArray()    for (filter in value.filters) {        out.beginObject()        out.name("filterType").value(filter.filterType)        out.name("value").value(filter.identifier)        out.endObject()    }    out.endArray()    out.name("versionCode").value(value.versionCode.toLong())    if (value.versionName != null) {        out.name("versionName").value(value.versionName)    }    out.name("enabled").value(value.isEnabled)    if (value.filterName != null) {        out.name("filterName").value(value.filterName)    }    if (value.outputFileName != null) {        out.name("outputFile").value(value.outputFileName)    }    out.name("fullName").value(value.fullName)    out.name("baseName").value(value.baseName)    out.endObject()}

json字符串的构造其实是依赖于goole自定义的ApkInfoAdapter适配器类来实现的,具体不说啦。
今天就到这吧。。。

更多相关文章

  1. Android调用系统自带的文件管理器,打开指定路径
  2. Gallery中重复文件夹的问题
  3. android sqlite3 数据库升级,加字段
  4. Android Studio Gradle 缓存文件夹设置
  5. Android内核驱动开发中的Kconfig文件结构分析(图文)
  6. Android震动和播放资源文件中的声音文件
  7. Android文件管理器开发对各类文件的打开以及处理

随机推荐

  1. Android判断是否是平板
  2. android中布局使用大全
  3. android 用代码画虚线边框背景
  4. Android(安卓)相对布局
  5. Android(安卓)API Level对应Android版本
  6. android 实现透明按钮
  7. Android隐藏状态栏和标题栏,相当于全屏效
  8. Android剖析和运行机制
  9. 系统自带theme
  10. android 布局居中