乐趣区

关于android:Android-Gradle必备基础知识

随着 GoogleEclipse的有情摈弃以及 Studio 的一直壮大,Android开发者逐步拜倒在 Studio 的石榴裙下。
而作为 Studio 的默认编译形式,Gradle已逐步遍及。我最开始是被它的多渠道打包所吸引。接下来咱们就零碎的学习一下Gradle

简介

Gradle是以 Groovy 语言为根底,面向 Java 利用为主。基于 DSL(Domain Specific Language) 语法的自动化构建工具。

Gradle汇合了 Ant 的灵活性和弱小性能,同时也汇合了 Maven 的依赖治理和约定,从而发明了一个更无效的构建形式。凭借 GroovyDSL和翻新打包形式,Gradle提供了一个可申明的形式,并在正当默认值的根底上形容所有类型的构建。Gradle目前已被选作许多开源我的项目的构建零碎。

因为 Gradle 是基于 DSL 语法的,如果想看到 build.gradle 文件中全副能够选项的配置,能够看这里
DSL Reference

根本的我的项目设置

一个 Gradle 我的项目通过一个在我的项目根目录中的 build.gradle 文件来形容它的构建。

简略的 Build 文件

最简略的 Android 利用中的 build.gradle 都会蕴含以下几个配置:
Project根目录的build.gradle:

buildscript {
    repositories {jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.5.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

Module中的build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
    ...
}
  • buildscript {...}配置了编译时的代码驱动. 这种状况下,它申明所应用的是 jCenter 仓库。还有一个申明所依赖的在 Maven 文件的门路。这里申明的蕴含了 Android 插件所应用的 1.5.0 版本的 Gradle. 留神: 这只会影响build 中运行的代码,不是我的项目中。我的项目中须要申明它本人所须要仓库和依赖关系。
  • apply plugin : com.android.application,申明应用 com.androdi.application 插件。这是构建 Android 利用所须要的插件。
  • android{...}配置了所有 Android 构建时的参数。默认状况下,只有编译的指标版本以及编译工具的版本是须要的。

重要: 这里只能应用 com.android.application 插件。如果应用 java 插件将会报错。

目录构造

module/src/main下的目录构造,因为有时候很多人把 so 放到 libs 目录就会报错:

  • java/
  • res/
  • AndroidManifest.xml
  • assets/
  • aidl/
  • jniLibs/
  • jni/
  • rs/

配置目录构造

如果我的项目的构造不规范的时候,可能就须要去配置它。Android插件应用了类似的语法,然而因为它有本人的 sourceSets,所以要在android 代码块中进行配置。上面就是一个从 Eclipse 的老我的项目构造中配置次要代码并且将 androidTestsourceSet设置给 tests 目录的例子:

android {
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        androidTest.setRoot('tests')
    }
}

就像有些人就是要把 so 放到 libs 目录中 (这类人有点犟),那就须要这样进行批改。
留神: 因为在旧的我的项目构造中所有的源文件 (Java,AIDLRenderScript)都放到同一个目录中,咱们须要将 sourceSet 中的这些新部件都设置给 src 目录。

Build Tasks

对构建文件申明插件时通常或主动创立一些列的构建工作去执行。不论 Java 插件还是 Android 插件都是这样。Android惯例的工作如下:

  • assemble生成我的项目 output 目录中的内容的工作。
  • check执行所有的查看的工作。
  • build执行 assemblecheck的工作。
  • clean清理我的项目 output 目录的工作。

Android 我的项目中至多会有两种 output 输入: 一个 debug apk 和一个release apk。他们都有本人的主工作来别离执行构建:

  • assemble

    • assembleDebug
    • assembleRelease

提醒:Gradle反对通过命令行执行工作首字母缩写的形式。例如:
在没有其余工作合乎 aR 的前提下,gradle aRgradle assembleRelease 是雷同的。

最初,构建插件创立了为所有 build type(debug, release, test) 类型装置和卸载的工作,只有他们能被装置(须要签名)。

  • installDebug
  • installRelease
  • uninstallAll

    • uninstallDebug
    • uninstallRelease
    • uninstallDebugAndroidTest

根本的 Build 定制

Android插件提供了一些列的 DSL 来让间接从构建零碎中做大部分的定制。

Manifest整体局部

DSL提供了很多重要的配置 manifest 文件的参数,例如:

  • minSdkVersion
  • targetSdkVersion
  • versionCode
  • versionName
  • applicationId
  • testApplicationId
  • testInstrumentationRunnder

Android Plugin DSL Reference 提供了一个残缺的构建参数列表。

把这些 manifest 属性放到 build 文件中的一个重要性能就是它能够被动静的设置。例如,能够通过读取一个文件或者其余逻辑来获取版本名称。

def computeVersionName() {...}

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"


    defaultConfig {
        versionCode 12 
        versionName computeVersionName()
        minSdkVersion 16
        targetSdkVersion 23
    }
}

留神: 不要应用可能与现有给定抵触的办法名。例如 defaultConfig{...} 中应用 getVersionName() 办法将会主动应用 defaultConfig.getVersionName() 来带起自定义的办法。

Build Types

默认状况下 Android 插件会主动将应用程序设置成有一个 debug 版本和一个 release 版本。
这就是通过调用 BuildType 对象实现。默认状况下会创立两个实例,一个 debug 实例和一个 release 实例。Android插件同样容许通过其余的 Build Types 来定制其余的实例。这就是通过 buildTypes 来设置的:

android {
    buildTypes {
        debug {applicationIdSuffix ".debug"}


        jnidebug {initWith(buildTypes.debug)
            applicationIdSuffix ".jnidebug"
            jniDebuggable true
        }
    }
}

下面的代码执行了以下操作:

  • 配置了默认 debugBuild Type:

    • 设置了它的 applicationId。这样debug 模式就能与 release 模式的 apk 同时装置在同一手机上。
  • 创立了一个新的 jnidebugBuild Type,并且把它设置为 debug 的拷贝。
  • 通过容许 JNI 组件的 debug 和减少一个新的包名后缀来持续定制该Build Type

不论应用 initWith() 还是应用其余的代码块,创立一个新的 Build Types 都是非常简单的在 buildTypes 代码块中创立一个新的元素就能够了。

签名配置

为利用签名须要应用如下几个局部:

  • A keystore
  • A keystore password
  • A key alias name
  • A key password
  • The store type

默认状况下有一个 debug 的配置,设置了一个 debugkeystore,有一个已知的明码。debug keystore的地位是在 $HOME/.android/debug.keystore,如果没有的话他会被默认创立。DebugBuild Type会默认应用该 debug 的签名设置。

当然也能够通过应用 DSL 语法中的 signingconfigs 局部来创立其余的配置来进行定制:

android {
    signingConfigs {
        debug {storeFile file("debug.keystore")
        }


        myConfig {storeFile file("other.keystore")
            storePassword "android"
            keyAlias "androiddebugkey"
            keyPassword "android"
        }
    }


    buildTypes {
        foo {signingConfig signingConfigs.myConfig}
    }
}

下面的设置将把 debug keystore 的地位改为我的项目的根目录。同样也创立了一个新的签名配置,并且有一个新的 Build Type 应用它。

Dependencies, Android Libraries and Multi-project setup

Gradle我的项目能够依赖其余的内部二进制包、或者其余的 Gradle 我的项目。

本地包

想要配置依赖一个内部 jar 包,须要在 compile 的配置中增加一个 dependency。上面的配置是增加了所有在libs 目录的 jar 包:

dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])
}


android {...}

留神:DSL元素中的 dependenciesGradle API中的规范元素。不属于 andorid 元素。
compile配置是用来编译主利用的。它配置的所有局部都会被打包到 apk 中。当然也有一些其余的配置:

  • compile: main application
  • androidTestCompile:test application
  • debugCompile:debug Build Type
  • release Compile:release Build Type

当然咱们能够应用 compile<buildtype>.compile这两种配置。创立一个新的 Build Type 通常会主动基于它的名字创立一个新的配置局部。这样在像 debug 版本而 release 版本不实用的一些特地的 library 时十分有用。

近程仓库

Gradle只是应用 MavenIvy仓库。然而仓库必须要增加到列表中,并且必须申明所依赖仓库的 Maven 或者 Ivy 定义。

repositories {jcenter()
}


dependencies {compile 'com.google.guava:guava:18.0'}


android {...}

留神:jcenter()是指定仓库 URL 的快捷设置。Gradle反对近程和本地仓库。
留神:Gradle会间接辨认所有的依赖关系。这就意味着如果一个依赖库本身又依赖别的库时,他们会被一起下下来。

本地 AAR
dependencies {compile(name:'本地 aar 库的名字,不必加后缀', ext:'aar')
}
多我的项目设置

Gradle我的项目通常应用多我的项目设置来依赖其余的 gradle 我的项目。例如:

  • MyProject/

    • app/
    • libraries/

      • lib1/
      • lib2/

Gradle会通过上面的名字来援用他们:
:app
:libraries:lib1
:libraries:lib2

每个我的项目都会有一个独自的 build 文件,并且在我的项目的根目录还会有一个 setting.gradle 文件:

  • MyProject/

    • settings.gradle
    • app/

      • build.gradle
    • libraries/

      • lib1/

        • build.gradle
      • lib2/

        • build.gradle

setting.gradle文件中的内容非常简单。它指定了哪个目录是 Gralde 我的项目:

include ':app', ':libraries:lib1', ':libraries:lib2'

:app这个我的项目可能会依赖其余的libraries,这样能够通过如下进行申明:

dependencies {compile project(':libraries:lib1')
}

Library我的项目

下面用到了 :libraries:lib1:libraries:lib2能够是 Java 我的项目,:app我的项目会应用他们俩的输入的 jar 包。然而如果你须要应用 android 资源等,这些 libraries 就不能是一般的 Java 我的项目了,他们必须是 Android Library 我的项目。

创立一个 Library 我的项目

Library我的项目和一般的 Android 我的项目的区别比拟少,因为 libraries 的构建类型与应用程序的构建不同,所有它会应用一个别的构建插件。然而他们所应用的插件外部有很多雷同的代码,他们都是由 com.android.tools.build.gradle 这个 jar 包提供的。

buildscript {
    repositories {jcenter()
    }


    dependencies {classpath 'com.android.tools.build:gradle:1.3.1'}
}


apply plugin: 'com.android.library'


android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
}
一般我的项目与 Library 我的项目的区别

Library我的项目的次要输入我 .aar 包。它联合了代码 (例如jar 包或者本地 .so 文件)和资源 (manifest,res,assets)。每个library 也能够独自设置 Build Type 等来指定生成不同版本的aar

Lint Support

你能够通过指定对应的变量来设置 lint 的运行。能够通过增加 lintOptions 来进行配置:

android {
    lintOptions {
        // turn off checking the given issue id's
        disable 'TypographyFractions','TypographyQuotes'

        // turn on the given issue id's
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'

        // check *only* the given issue id's
        check 'NewApi', 'InlinedApi'
    }
}

Build变量

构建零碎的一个指标就是能对同一个利用创立多个不同的版本。

Product flavors

一个 product flavor 能够针对一个我的项目制订不同的构建版本。一个利用能够有多个不同的 falvors 来扭转生成的利用。
Product flavors是通过 DSL 语法中的 productFlavors 来申明的:

android {
    ....


    productFlavors {
        flavor1 {...}


        flavor2 {...}
    }
}
Build Type + Product Flavor = Build Variant

像咱们之前看到的,每个 Build Type 都会生成一个 apk.Product Flavors 也是同样的:我的项目的输入僵尸所有 Build TypesProduct Flavors的联合。每种联合形式称之为 Build Variant。例如,如果有debugrelease版本的Build Types,下面的例子就会生成 4 种Build Variants

  • Flavor1debug
  • Flavor1release
  • Flavor2debug
  • Flavor2release

没有配置 flavors 的我的项目依然有 Build Variants,它只是用了一个默认的flavor/config,没有名字,这导致variants 的列表和 Build Types 的列表比拟雷同。

Product Flavor配置
android {
    ...


    defaultConfig {
        minSdkVersion 8
        versionCode 10
    }


    productFlavors {
        flavor1 {
            applicationId "com.example.flavor1"
            versionCode 20
         }


         flavor2 {
             applicationId "com.example.flavor2"
             minSdkVersion 14
         }
    }
}

留神 android.productFlavors.* 对象 ProductFlavorandroid.defaultConfig是雷同的类型。这就意味着他们有雷同的属性。
defaultConfig为所有的 flavors 提供了一些根本的配置,每个 flavor 都已重写他们。在下面的例子中,这些配置有:

  • flavor1

    • applicationId: com.example.flavor1
    • minSdkVersion: 8
    • versionCode: 20
  • flavor2

    • applicationId: com.example.flavor2
    • minSdkVersion: 14
    • versionCode: 10

通常,Build Type配置会笼罩其余的配置。例如,Build TypeapplicationIdSuffix 会增加到 Product FlavorapplicationId上。

最初,就像 Build Types 一样,Product Flavors也能够有他们本人的依赖关系。例如,如果有一个独自的 flavors 会应用一些广告或者领取,那这个 flavors 生成的 apk 就会应用广告的依赖,而其余的 flavors 就不须要应用。

dependencies {flavor1Compile "..."}

BuildConfig

在编译阶段,Android Studio会生成一个叫做 BuildConfig 的类,该类蕴含了编译时应用的一些变量的值。你能够观看这些值来扭转不同变量的行为:

private void javaCode() {if (BuildConfig.FLAVOR.equals("paidapp")) {doIt();
    else {showOnlyInPaidAppDialog();
    }
}

上面是 BuildConfig 中蕴含的一些值:

  • boolean DEBUGif the build is debuggable
  • int VERSION_CODE
  • String VERSION_NAME
  • String APPLICATION_ID
  • String BUILD_TYPEBuild Type的名字,例如release
  • String FLAVORflavor的名字,例如flavor1

ProGuard配置

Android插件默认会应用 ProGuard 插件,并且如果 Build Type 中应用 ProGuardminifyEnabled属性开启的话,会默认创立对应的task

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFile getDefaultProguardFile('proguard-android.txt')
        }
    }

    productFlavors {flavor1 {}
        flavor2 {proguardFile 'some-other-rules.txt'}
    }
}

Tasks管制

根本的 Java 我的项目有一系列的 tasks 一起制作输入文件。
classes task就是编译 Java 源码的工作。咱们能够在 build.gradle 中通过应用 classes 很简略的获取到它。就是project.tasks.classes.

Android 我的项目中,更多的编译 task,因为他们的名字通过Build TypesProduct Flavors生成。

为了解决这个问题,android对象有两种属性:

  • applicationVariantsonly for the app plugin
  • libraryVariantsonly for the library plugin
  • testVariantsfor both plugins
    这些都会返回一个 ApplicationVariant, LibraryVariant,TestVariantDomainObjectCollection接口的实现类对象。
    DomainObjectCollection提供了间接获取或者很不便的间接获取所有对象的办法。

    android.applicationVariants.all { variant ->
     ....
    }

设置编译语言版本

能够应用 compileOptions 代码块来设置编译时应用的语言版本。默认是基于 compileSdkVersion 的值。

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_6
        targetCompatibility JavaVersion.VERSION_1_6
    }
}

Resource Shrinking

Gradle构建零碎反对资源清理:对构建的利用会主动移除无用的资源。不仅会移除我的项目中未应用的资源,而且还会移除我的项目所以来的类库中的资源。留神,资源清理只能在与代码清理联合应用(例如ProGuad)。这就是为什么它能移除所依赖类库的无用资源。通常,类库中的所有资源都是应用的,只有类库中无用代码被移除后这些资源才会变成没有代码援用的无用资源。

android {
    ...

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
退出移动版