Android gradle Plugin


参考

https://developer.android.com/studio/build

http://google.github.io/android-gradle-dsl/current/index.html

https://developer.android.com/reference/tools/gradle-api/classes

说明

学习gradle最烦人的就是api不知道去哪查询,想要实现某个打包功能只能去网上copy人家的,

gradle官方的文档还好,但Android gradle plugin的官方文档却更新不及时,原来的文档一直是3.4的,

现在竟然完全删除了,真tm狗东西,现在只有api了,想要深入只能通过complieOnly导入gradle依赖,下边有说明。

android gradle plugin版本查询

https://developer.android.com/studio/releases/gradle-plugin

gradle plugin和gradle版本对应

Plugin version

Required Gradle version

1.0.0 - 1.1.3

2.2.1 - 2.3

1.2.0 - 1.3.1

2.2.1 - 2.9

1.5.0

2.2.1 - 2.13

2.0.0 - 2.1.2

2.10 - 2.13

2.1.3 - 2.2.3

2.14.1+

2.3.0+

3.3+

3.0.0+

4.1+

3.1.0+

4.4+

3.2.0 - 3.2.1

4.6+

3.3.0 - 3.3.3

4.10.1+

3.4.0 - 3.4.3

5.1.1+

3.5.0 - 3.5.4

5.4.1+

3.6.0 - 3.6.4

5.6.4+

4.0.0+

6.1.1+

4.1.0+

6.5+

Extension types

Listed below are the Gradle extension types used by respective plugins:

Type

Description

AppExtension

The android extension for application plugins.

LibraryExtension

The android extension for com.android.library projects.

TestExtension

android extension for com.android.test projects.

FeatureExtension

The android extension for com.android.feature projects.

aaptOptions { }

Specifies options for the Android Asset Packaging Tool (AAPT).

adbOptions { }

Specifies options for the Android Debug Bridge (ADB), such as APK installation options.

buildTypes { }

Encapsulates all build type configurations for this project.

compileOptions { }

Specifies Java compiler options, such as the language level of the Java source code and generated bytecode.

dataBinding { }

Specifies options for the Data Binding Library.

defaultConfig { }

Specifies defaults for variant properties that the Android plugin applies to all build variants.

dexOptions { }

Specifies options for the DEX tool, such as enabling library pre-dexing.

externalNativeBuild { }

Configures external native build using CMake or ndk-build.

jacoco { }

Configure JaCoCo version that is used for offline instrumentation and coverage report.

lintOptions { }

Specifies options for the lint tool.

packagingOptions { }

Specifies options and rules that determine which files the Android plugin packages into your APK.

productFlavors { }

Encapsulates all product flavors configurations for this project.

signingConfigs { }

Encapsulates signing configurations that you can apply to BuildType and ProductFlavor configurations.

sourceSets { }

Encapsulates source set configurations for all variants.

splits { }

Specifies configurations for building multiple APKs or APK splits.

testOptions { }

Specifies options for how the Android plugin should run local and instrumented tests.

AppExtension

其实对应的就是app中的build.gradle中的android,它对应的类型是BaseAppModuleExtension,继承关系如下:

  • BaseAppModuleExtension实现了AppExtensionApplicationExtension
  • ApplicationExtension实现了 CommonExtensionApkExtensionTestedExtension

BaseAppModuleExtension是内部的api,在api文档中被隐藏了,其实大部分的属性方法都是在CommonExtension中定义的。

variantFilter

https://developer.android.com/studio/build/build-variants.html#filter-variants

variantFilter(Action filter)

在这里你可以对variant进行过滤,通过调用VariantFilter.setIgnore(true)。

android {
    ...
    variantFilter { variant ->

        def buildTypeName = variant.buildType*.name
        def flavorName = variant.flavors*.name

        if (flavorName.contains("dev") && buildTypeName.contains("release")) {
            // Tells Gradle to ignore each variant that satisfies the conditions above.
            setIgnore(true)
        }
    }
}

applicationVariants

DomainObjectSet applicationVariants

ApplicationVariant在com.android.build.gradle.api.ApplicationVariant

你可以在这里获取到最终的可构建的variant,你可以在此处对各variant进行属性修改。

另外由于此集合时DomainObjectSet,所以应该使用.all来遍历,而不是each,原因是插件只在project is evaluated之后才填充此集合。

android.applicationVariants.all { variant ->
    StringBuilder sb = new StringBuilder()
    for (def productFlavor : variant.productFlavors) {
        sb.append(productFlavor.name)
    }
    println "productFlavor:$sb, buildType:$variant.buildType.name, applicationId:$variant.applicationId"

    def mergedFlavor = variant.getMergedFlavor()
    if ("debug".equalsIgnoreCase(variant.buildType.name)) {
        mergedFlavor.applicationId = "com.fuck.you"
        mergedFlavor.manifestPlaceholders = [hostName:"www.example.com/${variant.versionName}"]
    }
}

通过variant.getMergedFlavor()获取最后组合的flavor,通过这个productFlavor可以修改最后的包名、placeholder等等属性。

自定义打包后要输出的目录

def releaseTime() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

android{
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                File outputDirectory = new File(outputFile.parent);
                def fileName
                if (variant.buildType.name == "release") {
                    fileName =  "app_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
                } else {
                    fileName = "app_v${defaultConfig.versionName}_${packageTime()}_debug.apk"
                }
                output.outputFile = new File(outputDirectory, fileName)
            }
        }
    }
}

在Android Gradle Plugin 4.x中Variant新的配置方式

https://medium.com/androiddevelopers/new-apis-in-the-android-gradle-plugin-f5325742e614

以下是Android项目构建所经历的阶段的概述:

1. 运行Build scripts,允许构建和插件配置Android Gradle Plugin DSL对象以及注册variant 回调。

2. Android Gradle Plugin 结合了build types和product flavors来创建variants 和test components。

3. 将为每个variant调用onVariants,从而允许自定义variant设置,并为每个测试组件均调用onTestComponent。

4. 将为每个启用的variant调用onVariantProperties,并为每个启用的测试组件调用onTestComponentProperties,从而允许注册使用或修改构建中间件的任务。

5. 在调用onVariants和onVariantProperties回调后,Android Gradle Plugin 插件会为variant注册task。

6. 使用已注册task,为每个variant调用先前的variant API。

7. Gradle计算task graph,构建即可开始执行。

import com.android.build.api.variant.impl.ApplicationVariantPropertiesImpl
import com.android.build.api.variant.impl.ApplicationVariantImpl
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension

apply plugin: 'com.android.application'

android { BaseAppModuleExtension appModuleExtension ->
    onVariants { ApplicationVariantImpl applicationVariant ->

        StringBuilder sb = new StringBuilder()
        sb.append("name:" + applicationVariant.name + ", ")
        sb.append("flavorName:" + applicationVariant.flavorName + ", ")
        sb.append("productFlavors:" + applicationVariant.productFlavors + ", ")
        sb.append("buildType:" + applicationVariant.buildType + ", ")
        println sb
    }

    onVariantProperties { ApplicationVariantPropertiesImpl applicationVariantProperties ->
        StringBuilder sb = new StringBuilder()
        sb.append("productFlavor:$applicationVariantProperties.name, ")
        sb.append("buildType:$applicationVariantProperties.buildType, ")
        sb.append("applicationId:" + applicationVariantProperties.applicationId.get() + ", ")
        println sb


        // API 'MergedFlavor.getApplicationId()' is obsolete and has been replaced with 'VariantProperties.getApplicationId()'.
        // It will be removed in version 5.0 of the Android Gradle plugin.
        if ("debug".equalsIgnoreCase(applicationVariantProperties.buildType)) {
            applicationVariantProperties.applicationId.set(mlwDebugApplicationId)
        }

        //修改打包的名字
        applicationVariantProperties.outputs.each { output ->
            def date = new Date().format('yyyy_MMdd_HHmm')
            def file = output.outputFileName.get()
            def fileName = file.replace(".apk", "_" + android.defaultConfig.versionName + "_" + date + ".apk").replaceFirst("app", "rent_customer").replace("-", "_")
            output.outputFileName.set(fileName)
            //        println "output.outputFileName:" + output.outputFileName
        }
    }
}

上边的导入的api需要添加依赖才不会变红

compileOnly group: 'com.android.tools.build', name: 'gradle', version: '4.1.0'

在 Android Gradle Plugin 5.0中R.xxx不再是final

https://sites.google.com/a/android.com/tools/tips/non-constant-fields

Resource IDs will be non-final in Android Gradle Plugin version 5.0, avoid using them in switch case statements

一般我们会在onClick中使用switch(id)来检索对应操作,

public void onClick(View v) {
    switch (v.getId()) {
        case R.id.iv_close: {
            dismiss();
            break;
        }
        case R.id.bt_share: {
            if (onClickListener != null) {
                onClickListener.onClick(v);
            }
            dismiss();
            break;
        }
    }
}

但在Android Gradle Plugin version 5.0中,资源id都不是final了,而switch在编译器就把R.id.xx的值写入到class中,不是final就意味着可能发生改变,那么在运行时的时候可能就找不到对应的case了,

解决方法就是:

把switch换成if else,在as中选中switch关键字,然后键盘输入show context action的快捷键(我的是alt+enter),就会弹出快捷转换操作。