android gradle使用详解

我们知道Android studio在发布之初就使用了gradle来构建和管理Android项目,所以很多人在开发Android应用的过程中或多或少都和它打过交道,今天就给大家分享下我对gradle的一些理解,以及在Android开发中使用gradle的一些经验。

项目自动化构建工具的发展

最早在开发的过程中是没有项目自动化构建这个东西的,每次编译项目的时候都是在命令行下对每个源文件执行编译命令,这种方式对于源文件不多的小项目还行,但是当项目比较大有成百上千个源文件需要编译时就比较痛苦了,所以才有了一些自动化构建工具的诞生,其本质是将一些繁琐的无须人工干预的编译流程交由机器来完成。

Makefile

最早出现的构建工具是makefile,它主要用于C/C++项目,大家会发现Android的源码里面用的就是这一套构建机制。makefile文件将程序编译,链接,装载(编译原理上的东西,不熟悉的可以去翻阅相关书籍)的流程定义成一套统一的规则。其中就包括了:哪些源文件需要编译,如何去编译,依赖的库文件,以及如何生成最终的可执行文件等等。通过这些规则去实现我们的构建需求,那么当你在编译整个工程的时候就只需要在命令行下执行一个make命令就可以搞定了,极大的提高了项目的构建效率。

Ant

ant也是一套构建工具,主要应用于Java项目(在eclipse上开发Android项目的时候用的比较多)。它是一个将软件编译,测试,部署过程组织起来自动化执行的工具。ant构建文件基于xml,每个文件对应一个唯一的project,每个project下面可以有很多的target,这些target之间存在着一定的依赖关系,当执行某个target时需要先执行该target的依赖。每个target里面又包含了一些task,task就是最终需要执行的命令。

Maven

ant虽然能大幅提高构建的效率,但是也存在一些缺点,比方说ant中的组件依赖(jar包)不能跨网络使用,为了解决这个问题,于是maven出现了,maven使用了强大的中央仓库,使得项目中使用到的一些公共组件可以很方便的联网依赖和更新,这也极大的方便了一些开源项目的使用。

Gradle

由于maven的配置过于复杂和繁琐,于是出现了我们今天的主角gradle,下面给大家看下两者配置文件的对比。

maven的配置:

<dependencies>
    <dependency>
        <groupId>com.crashlytics.sdk.android</groupId>
        <artifactId>crashlytics</artifactId>
        <version>2.5.5</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
</dependencies>

gradle的配置:

dependencies {
    compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar')
    testCompile('junit:junit:4.7')
}

Gradle

关于gradle的介绍和资料,网上有很多,这里给出我觉得写的非常好的一篇文章:《深入理解Android之Gradle》,该文章由《深入理解Android》的作者邓平凡撰写,质量很高。我大致总结一下我的理解:

  1. gradle基于groovy实现了一套编程框架,所以我们可以在gradle配置中通过编程的方式灵活的去配置我们的构建过程。
  2. gradle还是一种DSL领域相关语言,也就是行话,比方说sourceSets代表源文件集合等。基于这些行话我们可以很方便的建立一个模板,通过这些模板可以更加方便的去配置我们的构建过程。
  3. gradle本质上执行一系列项目构建过程,比方说Android项目里的check,build,assemble,install等。所以要更好的使用gradle需要先熟悉你正在开发项目的构建流程。

Gradle在Android中的实践

android studio中gradle配置详解

我们打开AndroidStudio选择Android视图,可以很清楚的看到当前项目下的gradle相关的配置文件,这里我们逐一介绍。


  • build.gradle(Project:nim_demo)为root project(这里我们称项目的根目录为root project)下的gradle配置文件,该配置文件一般用来做一些全局性的配置。
//全局配置构建工具的classpath和远程仓库路径,这里一般配置为gradle的,因为项目在构建过程中需要使用gradle去执行,当然如果你使用到了一些额外的插件,比如注解处理器,也可以放在这里。
buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.3'
    }
}
//这里是对项目中的所有project进行配置,以Android项目为例,这里的project包括项目下的各种module以及项目根目录root project。
allprojects {
    repositories {
        jcenter()
    }
}
//这里是对该project下的所有子project进行配置,以Android项目为例,一般我们可以把一些公用的操作放在一个gradle配置文件中,然后import到各个子project的gradle中,方便使用
subprojects {
//类似于Java中的import功能,将该文件中定义的方法或变量导入后,其他gradle文件可以直接引用该gradle中的方法或变量
    apply from: 'common.gradle'
}
//定义一些变量,该变量可以被其他gradle使用
ext {
    compileSdkVersion=21
    buildToolsVersion='23.0.2'
    minSdkVersion=9
    targetSdkVersion=19
    versionCode=28
    versionName='3.0.0'
    targetCompatibility=1.7
    sourceCompatibility=1.7
}
  • build.gradle(Module:demo)为module下的gradle配置,该配置只对该module生效。
//声明该模块最终生成产物为apk
apply plugin: 'com.android.application'

android {
//编译使用的sdk版本
    compileSdkVersion 23
    //使用的编译工具版本,一般与sdk版本对应,比如sdk version为23,则buildToolVersion应选择为23.x.x
    buildToolsVersion rootProject.buildToolsVersion
//所有flavor的默认配置
    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 23
    }
//project的签名配置
    signingConfigs {
        debug { storeFile file("debug.keystore") }

        release {
            storeFile file('release.keystore')
            storePassword 'thisiskeystorepassword'
            keyAlias 'nim_demo'
            keyPassword 'thisiskeypassword'
        }
    }
//project的编译类型
    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }

        release {
            minifyEnabled true
            zipAlignEnabled true
            proguardFile('proguard.cfg')
            signingConfig signingConfigs.release
        }
    }
    //项目的目录结构
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            aidl.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']

        }

    }
//lint检查的选项
    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }
    //生成dex的选项
    dexOptions {
        incremental true
        preDexLibraries false
        jumboMode true
        javaMaxHeapSize "4g"
    }
    //打包选项
    packagingOptions {
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
    }

}
//该project的依赖
dependencies {
//依赖本地libs目录下的jar包
    compile fileTree(dir: 'libs', include: '*.jar')
    //依赖uikit模块
    compile project(path: ':uikit')
}

这里的配置就是Android Gradle插件特有的配置,我们可以去官方网站上找到每个配置项的详细说明。

  • build.gradle(Module:uikit)为uikit模块下的配置,该配置只对uikit生效。
//声明该模块编译产物为aar
apply plugin: 'com.android.library'
//其余配置与dmeo模块类似,这里不再赘述
android {
    useLibrary 'org.apache.http.legacy'

    compileSdkVersion 23
    buildToolsVersion buildToolsVer

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res', 'res-ptr']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:23.3.0'
    compile 'com.android.support:design:23.3.0'
    compile fileTree(dir: 'libs', include: '*.jar')
}

  • gradle-wrapper.properties(Gradle Version)为gradle的版本配置信息。
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

当我们从github下载一个开源项目导入到Android Studio中打开时,如果本地的gradle版本与该配置文件的版本不符就会去联网下载对应版本的gradle。由于gradle有时访问会比较慢,所以建议将网上的项目中该配置文件的版本改成本地已经下载了的版本运行,可以节省时间。有时修改版本信息后可能会出现一些语法错误,这是本地的gradle版本不支持该语法,因此仍然需要重新下载对应的gradle版本。

  • gradle.properties(Project Properties)是与当前项目相关的一个配置文件,主要包括一些当前项目中需要用到的键值对信息。
#SDK下build工具的版本号
buildToolsVer=23.0.2

#app内部版本号 
verCode=16

#app外部版本号
verName=1.2

该属性文件会被gradle插件自动加载,所以在project的gradle文件中可以直接使用。当然你也可以自定义一个属性文件me.properties,然后在build.gradle文件中读取。

def getVer() {
    Properties properties = new Properties()
    File file = new File(rootDir.absolutePath + "/me.properties")
    properties.load(file.newDataInputStream())
    return properties.get("verName")//为了增加可读性,添加return
}
  • settings.gradle(Project Settins)为工程的设置文件,告诉gradle当前工程的组成部分,比如有多少个子工程或者模块。
include ':uikit' 
include ':demo'

该工程包含两个模块,一个是uikit,另一个是demo。

gradle中添加自定义task

前面我们说的都是gradle的一些配置过程,它们都是Android gradle插件已经提供好了的配置选项,我们只要参考官方文档,合理的配置就能完成绝大部分的要求,但是有时候我们的项目可能需要在构建过程中做一些特殊的处理,比方说拷贝一些文件,调试配置信息等,这就需要我们在gradle的配置中添加一些自定义的task来完成。要找到合适的添加时机,我们就必须了解gradle的工作流程。Gradle 工作流程主要包含三个阶段:

  • 首先是初始化阶段,一般来说就是执行setting.gradle
  • 然后是配置阶段,配置阶段会解析每个project下的build.gradle,建立一个DAG有向图来确定project中task之间的依赖关系。
  • 最后是根据之前建立的task开始执行task。


    image

    通过以上流程,我们发现gradle给我们提供了一些可以插入自己task的hook。

总结

gradle的东西暂时就讲这么多,后续如果有进一步的研究会再来分享。其实,对于我们来说一般能掌握到一些基本的gradle配置就可以满足大部分的项目需求,如果需要深入理解的话,就需要我们去研究groovy的语法和gradle的用户参考手册,来帮助我们实现更复杂的需求。

参考

android gradle插件文档

gradle官方文档

gradle用户手册

深入理解Android gradle

推荐阅读更多精彩内容