Android插件的Gradle基础

Posted carryoner

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android插件的Gradle基础相关的知识,希望对你有一定的参考价值。

1.Gradle简介

查看Gradle最新版本2.13

查看Gradle User Guide

Gradle是一个基于Java(JVM)平台的构建体系(build system),它的一些特性:

  • Gradle核心是基于Groovy的领域特定语言(Domain Specific Languages,DSL),具有良好的扩展性,所以不管是简单的独立项目还是大型的多项目构建它都能高效的提高构建任务,尤其对多项目支持是非常好;Gradle还提供了局部构建功能,譬如构建一个单独子项目时它会构建这个子项目依赖的所有子项目;当然了他对远程仓库和本地库的支持也很到位;
  • 基于依赖关系的编程语言(a language for dependency based programming),就是说你可以定义tasks间的关系,Gradle保证按照依赖关系的顺序执行tasks,在tasks执行之前Gradle的构建脚本(build scripts)会组建起一张依赖关系图;
  • Deep API,这些API为监控和自定义Gradle配置和执行行为提供了便利;
  • Groovy,Groovy是Gradle build script的脚本语言,为什么用Groovy?答案在于Gradle的应用场景,虽然说Gradle是一个通用的构建工具,但它主要还是立足于Java工程,而Groovy是基于Java的,所以选择Groovy就显得合情合理,因为对于Java开发者而言,学习成本最低;
  • 应用范围广,从单个project到大企业的multi-project;
  • 移植方便;

2.Gradle基础

2.1.Groovy

  • Groovy是面向Java平台的动态语言,有许多Python,Ruby语言的特性,它的语法与Java类似;
  • Groovy不仅仅是脚本语言,它可以预编译为Java字节码,整合到Java应用、web应用中,甚至自己可以作为整个应用的基础;
  • 在Groovy中一切皆对象;
  • DSL是一种特定领域的语言(功能领域、业务领域),Groovy是通用的编程语言,所以不是DSL,但是Groovy却对编写全新的DSL提供了很好的支持,这些支持来自于Groovy自身语法的特性:
    (1)Groovy不需用定义CLASS类就可以直接执行脚本;
    (2)Groovy语法省略括弧和语句结尾分号等操作。

讲一个概念closure(闭包),此处不讲Groovy的语法,深入学习Groovy

2.1.1.closure(闭包)

closure是被包装成对象的一段代码块,它可以像方法那样传入参数并返回一个值;
表现形式如下:

{ [closureParameters -> ] statements }

2.1.2.声明closure

1、简单声明
// Groovy object iteration
list.each { item -> /* do something with item */ }
2、利用赋值声明
def printer = { line -> println line }
3、应用方法作为closure
//利用reference.&操作符
class SizeFilter {
    Integer limit
    boolean sizeUpTo(String value) {
        return value.size() <= limit
    }
}
SizeFilter filter6 = new SizeFilter(limit:6)

Closure sizeUpTo6 = filter6.&sizeUpTo

2.1.3.调用closure

  • 闭包对象.call(参数)
  • 闭包对象(参数)
def adder = { x, y -> return x+y }
assert adder(4, 3) == 7
assert adder.call(2, 6) == 8
//在Groovy构建脚本中,方法调用可以写成如下形式
method argOne, keyOne: valueOne, argTwo, keyTwo: valueTwo, argThree
//Groovy‘s runtime将解释为:
method([keyOne: valueOne, keyTwo: valueTwo], argOne, argTwo, argThree)
//最终将会调用有如下签名的方法
def method(Map m, argOne, argTwo, argThree)
/**
*方法void buildscript(Closure configureClosure)将
*closure作为参
*/
buildscript {

        repositories {
        jcenter()
        }
        dependencies {
            /**
            *方法Dependency add(String configurationName,  
            *Object dependencyNotation)接受参数
            */
           classpath ‘com.android.tools.build:gradle:2.1.0‘
        }
    }

2.1.4.脚本与类

Groovy脚本会被编译成类。举例如下:
相对于传统的Java类,一个包含main方法的Groovy类可以如下书写:

class Main {                                    
    static void main(String... args) {          
        println ‘Groovy world!‘                 
    }
}

和Java一样,程序会从这个类的main方法开始执行,这是Groovy代码的一种写法,实际上执行Groovy代码完全可以不需要类或main方法,所以更简单的写法如下:

println ‘Groovy world!‘

上面这两中写法其实是一样的,具体我们可以通过如下命令进行编译为class文件:

import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {                     
    def run() {                                 
        println ‘Groovy world!‘                 
    }
    static void main(String[] args) {           
        InvokerHelper.runScript(Main, args)     
    }
}

可以看见,上面我们写的groovy文件编译后的class其实是Java类,该类从Script类派生而来(查阅API);可以发现,每个脚本都会生成一个static main方法,我们执行groovy脚本的实质其实是执行的这个Java类的main方法,脚本源码里所有代码都被放到了run方法中,脚本中定义的方法(该例暂无)都会被定义在Main类中。

2.1.5.从closure中返回

  • 闭包中最后一句被执行,它的结果作为返回值,被称作end return
  • return关键字返回

2.2.DSL(Domain Specific Languages)

2.2.1基础

Gradle脚本是配置脚本(configuration scripts),当脚本执行的时候会创建一个关联对象(delegate object of script)。比如说,构建脚本(build script)执行的时候,它会配置关联对象project。

Type of script Delegates to instance of
Init script Gradle
Build script Project
Settings script Settings

2.2.2.三种主要对象(Project、Gradle、setting)

Gradle

构建初始化时创建,整个构建执行过程中只有这么一个对象,一般很少去修改这个默认配置脚本

Settings

每个settings.gradle会转换成一个Settings对象

Project

在Gradle中每个build.gradle对应一个Project对象,每个Project在构建的时候都包含一系列Task。

在脚本中可以使用关联对象(project、Gradle、settings)的属性(properties)和方法(methods),因为Gradle脚本实现了接口Script,所以接口Script的属性和方法也可以在脚本中使用

2.2.3.重要的API

所谓的我们编写Gradle脚本,实质大多数时候都是在编写构建脚本Build script,所以说 Project和Script对象的属性和方法等API非常重要

2.2.3.1.Script

所有的Gradle脚本都会实现Script接口,编译后的Script类会实现这个接口,接口Script的properties和methods可以在Gradle脚本中使用;

通常会有一个关联对象与Script对象关联;举例来说,一个构建脚本会将Project对象与它关联,一个初始化脚本会将Gradle对象与它关联,任何properties或者方法在Script对象中找不到,就需要去它的关联对象中去找。

2.2.3.2.Project

生命周期(lifecycle)

一个project在构建时都具备如下流程:

  • 创建一个Settings实例。

  • 评估settings.gradle脚本,通过该文件配置刚才创建的Settings实例。

  • 通过创建好的Settings实例创建Project实例的层级结构。

  • 最后通过上面创建的Project实例去执行每个Project对应的build.gradle脚本。

Tasks

project是一个task的集合,每一个task完成一份基本的工作(比如说编译类,进行单元测试)

dependencies

project为完成工作需要建立多个依赖关系,dependencyHandler用来管理dependencies;
这些依赖关系可以存在于configurations和从repositories中获取。

plugins

插件可以用来模块化和复用project configuration,通过PluginAware.apply()来运用plugin;

properties和methods

在脚本中可以使用接口project的任何方法和属性

2.3.构建生命周期(the build lifecycle)

2.3.1.初始化(Initialization)

Gradle支持单一或多个projects,在初始化的时候,Gradle决定哪些projects参与构建,并为每一个project创建一个Project实例;实质为执行settings.gradle.

2.3.2.配置(Configuration)

在这个时期,Project对象完成配置,将整个build的Project和task的关系确定,建立有向图来确定task间的依赖关系,实质为解析所有参与构建的projects的build.gradle;

2.3.3.执行(Execution)

Gradle开始执行选定的tasks及其依赖tasks,完成目标的构建;

2.4.构建脚本

构建脚本是由语句、代码块组成。

语句包括方法调用、属性赋值、变量定义;

代码块是调用以闭包(closure)为参数的方法;在执行的时候,闭包参数会配置相应的关联对象

3.Android Gradle插件(plugin)简介

Android Plugin for Gradle, Revision 2.1.0

Gradle和Android插件独立于Android Studio运行,也就是说在没有Android studio的情况下你也能构建你的Android App。Android构建体系让你能够自定义构建配置,而不用修改app的源码文件。

3.1构建过程(build process)

技术分享
典型的构建过程如图所示:

  • 编译器将源码转换成DEX文件(包含运行于Android设备中的字节码),其余的都加入到编译过的资源中。
  • APK Packager将.dex文件和编译的resource文件整合到单个APK中,但在安装和发布到Android设备之前,APK需要签名。
  • APK Packager给APk签名:如果你构建debug版的,Android Studio使用debug的keystore自动配置;如果构建release版的,就需要使用release keystore对apk进行签名,创建release keystore,请阅读Android Studio签名app
  • 在生成最终版的APK之前,packager运用zipalign工具会对你的app进行优化:减少运行时占用的内存。

3.2.Android中的Gradle构建文件(Gradle build files)

新建一个工程,Android Studio会自动创建一些文件,如下图
技术分享

3.2.1.顶层的构建文件(top-level build file)

位于project的根目录下,所有模块(整个project)的构建文件

/**
*buildscript {} 为Gradle配置仓库和依赖,例如将Android插件添加为依赖,
*modules自己的依赖不在这里配置。
*/
buildscript {


    /**
    *repositories {}配置repositories,Gradle用它来搜索或者下载依赖。
    *Gradle支持远程仓库(JCenter,Maven Central和Ivy),你也可以用本地的
    *仓库,代码中定义了JCenter作为仓库,Gradle需要用它来查找依赖。
    */

    repositories {
        jcenter()
    }
    dependencies {

        /**
        *dependencies {}配置依赖,Gradle用它来构建project,下面的代码将
        *2.1.0版Android插件作为路径依赖。
        */
        classpath ‘com.android.tools.build:gradle:2.1.0‘

    }
}
    /**
    *allprojects {}配置project中所有modules的repositories
    *和dependencies(例如第三方插件和库函数);
    *单个模块自己的依赖不能在这里配置,而是在module-level的build.gradle文件
    *中配置;
    *下面的代码将JCenter和mavenCentral作为仓库。
    */
    allprojects {

        repositories {
            jcenter()
            mavenCentral()
        }
}

3.2.2.模块的构建文件(module-level build file)

module-level build.gradle文件,配置module的构建。

/**
*第一行配置Android插件运用于构建,使得android {}可用于Android-specific的
*构建选项
*/  
apply plugin: ‘com.android.application‘

/**
*android {} 是配置Android-specific构建选项的地方
*/
android {

    /**
    * compileSdkVersion规定了Gradle编译使用的Android API版本
    * 即你可以使用比他低的API版本
    * buildToolsVersion规定了SDK build tools的版本
    * /

    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    /**
    *defaultConfig {}包含了所有构建版本的默认设置和条目,在动态构建的过程中
    *可以override掉main/AndroidManifest.xml一些属性
    */
    defaultConfig {

            /**
            * applicationId唯一的标识发布的package
            * /
            applicationId "io.rong.app"
            //app要求的最低的API版本
            minSdkVersion 9
            //测试app时使用的API版本
            targetSdkVersion 19
            //版本号
            versionCode 1
            //版本的名字
            versionName "2.4.10 Dev"
    }
    //配制project的结构
    sourceSets {
        main {
            jni.srcDirs = []
            jniLibs.srcDirs = [‘src/main/libs‘]
        }
    }

    signingConfigs {
        config {
            storeFile file("rong.key")
            storePassword "Amigo123"
            keyAlias "RongCloud"
            keyPassword "Amigo123"
        }
    }



    /**
    *buildTypes {}配置多个构建类型,默认的,构建体系定义了debug和release的
    *构建类型,构建类型设置Proguard是否可用
    */

    buildTypes {
        release {

            /**
            *默认的,Android Studio在release构建类型中使用Proguard
            *设置minifyEnabled为true
            */

            minifyEnabled true
            //签名的配置
            signingConfig signingConfigs.config
            //从Android SDK中运用默认的ProGuard的设置
            proguardFiles getDefaultProguardFile(‘proguard-android-optimize.txt‘),‘proguard-rules.pro‘
        }


        debug {
            minifyEnabled false
            shrinkResources false
            /**
            *将debug的构建类型的报名设为<app appliationId>.debug,
            *这样能够同时安装debug和release版的APK到相同的Android设备中
            */
            applicationIdSuffix ".debug"
        }
    }

    /**
    *productFlavors {}配置多个project flavors
    *它允许你创建不同版本的app,它可以override defaultConfig {} 中的设置
    *Product flavors是可选的设置,默认不配置
    *下面创建了免费和付费的product flavor,每一个product flavor会定义自己的
    *application ID,这样它们可以同时安装或者发布
    */
    productFlavors {
        free {
            applicationId ‘com.example.myapp.free‘
        }

        paid {
            applicationId ‘com.example.myapp.paid‘
        }
    }
    /**
    *lint工具的配置选项,Lint工具检查Android project的源文件中潜在
    *的bugs、改善优化correctness
    */
    lintOptions {
        checkReleaseBuilds false
    // Or, if you prefer, you can continue to check for errors in release builds,
    // but continue the build even when errors are found:
        abortOnError false
    }

}


//配置当前模块的依赖
dependencies {


    //告诉Gradle将所有JAR文件添加到app/libs folder
    compile fileTree(dir: ‘libs‘, include: [‘*.jar‘])

    compile ‘com.android.support:appcompat-v7:19.1.0‘
    compile ‘com.android.support:support-annotations:21.0.2‘
    compile project(‘:kit‘)

    compile files(‘libs/TencentSearch1.1.2.16095.jar‘)
    compile files(‘libs/TencentMapSDK_Raster_v1.1.2.16281.jar‘)
}

3.2.3.Gradle Properties文件

3.2.3.1.gradle.properties

配置project-wide的Gradle设置

3.2.3.2.local.properties

配置构建系统的本地环境,例如SDK的安装路径,它是Android Studio自动生成的,不要手动修改。

3.2.4.settings.gradle

gradle.settings文件告诉Gradle哪些modules需要参与构建app。

//这个文件引用了工程所有的模块
include ‘:app‘, ‘:lib‘, ‘:kit‘,‘thirdparty‘,‘:push‘,‘:toollib‘

3.3.构建自定义基础

构建自定义配置要求改变build.gradle文件,而这些文本文件使用DSL来描述和操作构建逻辑
,使用的脚本语言是Groovy,而你不需要掌握Groovy,你只需要掌握Android插件提供的DSL元素。

3.3.1.构建类型(build types)

构建类型定义了Gradle构建和打包app的特定properties;
默认的情况下,Android插件自动构建debug和release版的app;
debug构建类型用debug key来签名APK;而release的构建类型使用release key来进行发布;
你可以自定义其他类型的Build type;

android {
    ...
    defaultConfig {...}
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile(‘proguard-android.txt‘), ‘proguard-rules.pro‘
        }

        debug {
            applicationIdSuffix ".debug"
        }

        /**
         * ‘initWith‘属性允许你复用其他构建类型的配置,你只需要修改你想改变的
         * 配置,下面的例子中‘jnidebug’就使用了debug的build type,只是改变了
         * applicationIdSuffix和versionNameSuffix的设置
         */

        jnidebug {

            /** 
            * This copies the debuggable attribute and debug signing
            * configurations.
            * /
            initWith debug

            applicationIdSuffix ".jnidebug"
            jniDebuggable true
        }
    }
}

除了修改build的properties外,build types也可以添加特殊的sourceSet和resource。比如说每一个build type,其sourceSet被创建了,它的默认位置在src//,比如src/debug/java目录将被添加到debug版的APK

3.3.2.Product Flavors

Product flavors代表你要发布的不同版本,比如说免费和付费的版本,你可以添加sourceSet来增加一些特殊的属性;一个project可以有不同的flavors来构建不同的应用

android {
    ...
    defaultConfig {...}
    buildTypes {...}
    productFlavors {
        demo {
            applicationId "com.example.myapp.demo"
            versionName "1.0-demo"
        }
        full {
            applicationId "com.example.myapp.full"
            versionName "1.0-full"
        }
    }
}

3.3.3.构建不同版本(build variants)

Build Type + Product Flavor = Build Variant

默认的,Android Studio创建的main/ source set包含了所有build variants所共有的部分,然而build types和product flavors可以创建新的source set来控制Gradle编译和打包过程。举例来说,你可以在main/ sourceset中实现基本的功能,而用product flavor的sourceset来改变不同客户端的应用名以及debug build type的build variants的一些特殊的权限和日志功能。
build type,product flavor的sourceSet的结构与main/ sourceSet一样,除了根目录的名字是build type,product flavor的名字;举例来说,你在debug build type的sourceSet中创建manifest.xml文件:

MyProject/
    app/
        src/
            main/
            debug/
                java/
                res/
                // This manifest is only used with build variants that use
                // the debug build type.
                AndroidManifest.xml

Gradle根据build types和product flavors自动创建build variants,并且按照方式命名。例如,如果你创建了‘demo’和‘full’的product flavors,结合默认的‘debug’和‘release’构建类型,Gradle创建了下面的build variants:

demoDebug
demoRelease
fullDebug
fullRelease

你在构建demoDebug的build variant时,Gradle会找到这些目录,并且优先级从高到低:
src/demoDebug/ (build variant source set)
src/debug/ (build type source set)
src/demo/ (product flavor source set)
src/main/ (main source set)
这样做的原因是:demoDebug/ sourceSet中可能有build variant一些特殊的设置,demoDebug/和debug/中有相同的文件时,优先使用demoDebug/ sourceSet中的文件.
在构建过程中构建规则是:

  • 所有的在java/ 目录下的源码会被编译到一个文件中
  • Manifests整合进单个的manifest,优先级为:build type>product flavor>main>library
  • 相似地,values/中的文件整合到一起,如果有相同的文件,优先级和上面一样
  • 在res/和asset/目录下的资源也打包到一起,如果有相同的文件,优先级和上面一样
  • library中的资源和manifest被包括近来,它们优先级最低。

3.3.4.依赖(add dependencies)

下面的例子展示了app/ module’s build.gradle三种不同类型的依赖

android {...}
...
dependencies {
    // The ‘compile‘ configuration tells Gradle to add the dependency to the
    // compilation classpath and include it in the final package.

    // Dependency on the "lib" module from this project
    compile project(":lib")

    // Remote binary dependency
    compile ‘com.android.support:appcompat-v7:19.0.1‘

    // Local binary dependency
    compile fileTree(dir: ‘libs‘, include: [‘*.jar‘])
}
3.3.4.1.Module dependencies
/**
*将本地的Android library module添加为依赖,要求
*构建系统在构建app时将这个module包含进来并编译。
*/
compile project(":lib")
3.3.4.2.Remote binary dependencies
/**
*通过JCenter的协助将22.0.1版的Android support library
*添加为依赖,默认的,Android Studio在top-level的构建文件
*中使用JCenter仓库来配置projects,在你同步projects时Gradle
*会自动从JCenter中拉取dependency
*/
compile ‘com.android.support:appcompat-v7:22.0.1‘
3.3.4.3.Local binary dependencies
/**
*告诉构建体系将app/libs/目录下的JAR文件添加到
*app的包中,如果你的module建立本地的binary 
*dependencies,将此JAR文件复制到<moduleName>/libs中。
*/
compile fileTree(dir: ‘libs‘, include: [‘*.jar‘]) 

3.3.5.签名配置(signing configuration)

Gradle需要明确地为release build’s APK签名,请阅读用Android Studio签名release build type

debug mode:
开发时使用,Android SDK工具自动生成,证书(certificate)有一个private key和已知的password,每次app的改变和调试不需要输入password;
当你在Android Studio中运行或者调试project时自动以debug mode运行的
debug的配置使用debug keystore,位置在$HOME/.android/debug.keystore,debug build type默认使用这个。

release mode:
发布app时使用,使用自己的certificate:

  • 创建一个keystore,keystore是包含一组private keys的二进制文件
  • 创建private key标识app的条目(比如person或者company)
...
android {
    ...
    defaultConfig {...}
    signingConfigs {
        release {
            storeFile file("myreleasekey.keystore")
            storePassword "password"
            keyAlias "MyReleaseKey"
            keyPassword "password"
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}

Signing Your App in Android Studio

3.3.6.ProGuard

用来精简代码和resources,ProGuard可检测和移除无用的classes,fields,methods和属性;你可以自定义ProGuard规则,默认的该文件放在module的根目录下。

android {
    buildTypes {
        release {
            //使得ProGuard可用
            minifyEnabled true
            /**
            *getDefaultProguardFile(‘proguard-android.txt‘)从
            *Android SDK tools/proguard/文件中获取ProGuard的设置
            *proguard-rules.pro文件为自定义的的ProGuard文件,默认放在
            *module的根目录下
            */

            proguardFiles getDefaultProguardFile(‘proguard-android.txt‘),
                    ‘proguard-rules.pro‘
        }
    }
    ...
}

3.3.7.Lint

Lint工具检查Android project的源文件中潜在的bugs、改善优化correctness、security、performance、usability、accessiblility。

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‘
    }
}

3.4.Build Tasks

3.4.1.通用Tasks(general tasks)

构建文件中的插件会自动创建出一系列的任务,通常会有以下四种:
* assemble:负责project输出的task
* check:运行所有检查(checks)的task
* build:执行assemble和check的task
* clean:清除project输出的task
assemble、check、build task只是起到了’anchor’ tasks的作用,实际上不做任何事,添加与之关联的task来执行具体的任务。
这样做的原因是不管是什么样project、用什么样插件,只要执行assemble、check、build task,那么与之关联的task都会执行。

3.4.2Android插件中的anchor task

  • connectedCheck:进行要求连接到设备的检查
  • deviceCheck:进行连接远程设备检查

3.5.Library project

Library project和常规的Android project相似,但不同的地方在于Library project使用了不同的插件。

apply plugin: ‘com.android.library‘

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
}
...

Library project的主要输出是一个.arr包(代表着Android archive),它包含编译代码(a jar and/or native.so文件)和资源文件 (manifest, res, assets)。

引用Library
dependencies {
    compile project(‘:libraries:lib1‘)
    compile project(‘:libraries:lib2‘)
}































以上是关于Android插件的Gradle基础的主要内容,如果未能解决你的问题,请参考以下文章

Android Gradle 插件基础

Android插件的Gradle基础

Android Gradle 插件Gradle 基础配置 ⑥ ( Android工程 Gradle 常用命令 | gradlew 可执行文件 | 查看帮助文档 | 查看应用依赖 )

Android Gradle 插件Gradle 基础配置 ④ ( Gradle Wrapper 配置作用 | Gradle 下载的依赖库存放位置 )

Android Gradle插件开发基础

Android Gradle插件开发基础