Gradle总结

0x00 前言

Android studio默认使用Gradle构建Android工程。我之前有花一段时间研究Gradle,在此总结一下!
Gradle跟ant/maven一样,是一种依赖管理/自动化构建工具。但是跟ant/maven不一样,它并没有使用xml语言,而是采用了Groovy语言(语法类似于Java),这使得它更加简洁、灵活,更加强大。
Gradle的安装略,百度一大堆。

0x01 HelloWorld

先写个hello world吧!

task hello << {
    println 'Hello world!'
}

执行段语句:gradle -q hello,-q表示不显示log,相应的还可以 -info-debug来查看详细的log信息。

LogLevel

然后输出的内容是:Hello world!
这就是一个简单的gradle文件。里面定义了一个task:hello。执行打印任务。
当使用gradle命令行执行task时,每个task只会被执行一次,所以gradle test test和gradle test命令的执行结果是一模一样的。

0x02 Gradle Plugin

看了上面的例子,我们知道如何定义一个任务,但是在Android开发时我们看到的Gradle并没有定义task。因为在Android中我们默认添加了Gradle的Android插件:apply plugin: 'com.android.application'com.android.application中默认定义了我们一些常用的task:

build:    完整编译项目,包含编译,测试和创建jar包  
clean:    删除build目录 
assemble:  编译和创建jar包
check:    编译和测试代码

我们在一个gradle中可以使用多个插件。正如我们的项目使用crashlytics进行bug收集统计,那么我们的项目中还得依赖apply plugin: 'io.fabric'插件。

0x03 Android Gradle

在Android Studio新建Android项目现在默认使用gradle构建。使用gradle构建在规范的目录结构与以前ADT生成的有很大的不同了。Gradle要求的一些规范:工程源码位于$workspace/app/src/main/java,测试代码位于$workspace/app/src/test/java。所有位于$workspace/app/src/main/res文件都被包含到jar包中作为资源文件,所有的输出文件都会放在build目录里面,jar文件放在$workspace/app/build/libs目录下。
详细目录咱们可以通过命令gradlew app:sourceSets来查看:

main                           
----                           
Compile configuration: compile 
build.gradle name: android.sourceSets.main
Java sources: [app\src\main\java]
Manifest file: app\src\main\AndroidManifest.xml
Android resources: [app\src\main\res]
Assets: [app\src\main\assets]  
AIDL sources: [app\src\main\aidl]
RenderScript sources: [app\src\main\rs]
JNI sources: [app\src\main\jni]
JNI libraries: [app\src\main\jniLibs]
Java-style resources: [app\src\main\resources]

对于添加工程依赖也方便了很多,咱们可以通过dependencies来依赖工程,依赖生成的文件路径为:$workspace/app/build/intermediates/exploded-aar/***。依赖格式如下:

dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
}
缩写形式 “group:name:version”.
dependencies {
    compile 'org.hibernate:hibernate-core:3.6.7.Final'
}

note:-x命令用来排除一些命令的执行,比如gradle build -x ext,会在编译的时候不执行ext任务,即使build task依赖ext也不会执行, 但ext所依赖的task如果被其他task依赖是会执行的。
dependenciesDSL 元素是标准的 Gradle API 的一部分,不属于android 元素内。

dependencies {
    compile files('libs/test.jar')
}

0x04 Gradlew

在项目的根目录下我们看到有gradlew/gradlew.bat文件,gradlew代表 gradle wrapper,意思是gradle的一层包装,大家可以理解为在这个项目本地就封装了gradle,即gradle wrapper, 在$workspace/gradle/wrapper/gralde-wrapper.properties文件中声明了它指向的目录和版本。只要下载成功即可用grdlew wrapper的命令代替全局的gradle命令。第一次执行gradlew *时系统会下载创建项目时使用的gradle环境,时间会比较长。下载完后就可以直接像gradle一样使用了。

省略写法:gradle assembleRelease --> gradle aR

gradlew tasks
gradlew tasks --all
可以得到一个完整的任务列表,并且看到任务运行之间的依赖关系。

0x05 Gradle Build

Gradle编译过程如图:


build.png

我们可以查看gradle执行过程,比如我们要编译一个welove渠道的release包,执行gradle aWR >>1.txt>>1.txt表示将打印的内容重定向到记事本中,得到的内容:

:app:preBuild UP-TO-DATE
:app:preWeloveReleaseBuild UP-TO-DATE
:app:checkWeloveReleaseManifest
:app:preWeloveDebugBuild UP-TO-DATE
:app:prepareComAndroidSupportAppcompatV72200Library UP-TO-DATE
:app:preWeloveDebugAndroidTestBuild UP-TO-DATE
:app:prepareComAndroidSupportMultidex101Library UP-TO-DATE
:app:prepareComAndroidSupportRecyclerviewV72200Library UP-TO-DATE
:app:prepareComAndroidSupportSupportV42200Library UP-TO-DATE
:app:prepareWeloveReleaseDependencies
:app:compileWeloveReleaseAidl UP-TO-DATE
:app:compileWeloveReleaseRenderscript UP-TO-DATE
:app:generateWeloveReleaseBuildConfig UP-TO-DATE
:app:generateWeloveReleaseAssets UP-TO-DATE
:app:mergeWeloveReleaseAssets UP-TO-DATE
:app:generateWeloveReleaseResValues UP-TO-DATE
:app:generateWeloveReleaseResources UP-TO-DATE
:app:mergeWeloveReleaseResources UP-TO-DATE
:app:processWeloveReleaseManifest UP-TO-DATE
:app:processWeloveReleaseResources UP-TO-DATE
:app:generateWeloveReleaseSources UP-TO-DATE
:app:compileWeloveReleaseJavaWithJavac UP-TO-DATE
:app:compileWeloveReleaseNdk UP-TO-DATE
:app:compileWeloveReleaseSources UP-TO-DATE
:app:lintVitalWeloveRelease
:app:processWeloveReleaseJavaRes UP-TO-DATE
:app:transformResourcesWithMergeJavaResForWeloveRelease UP-TO-DATE
:app:transformClassesAndResourcesWithProguardForWeloveRelease UP-TO-DATE
:app:collectWeloveReleaseMultiDexComponents UP-TO-DATE
:app:transformClassesWithMultidexlistForWeloveRelease UP-TO-DATE
:app:transformClassesWithDexForWeloveRelease UP-TO-DATE
:app:mergeWeloveReleaseJniLibFolders UP-TO-DATE
:app:transformNative_libsWithMergeJniLibsForWeloveRelease UP-TO-DATE
:app:validateReleaseConfigSigning
:app:packageWeloveRelease UP-TO-DATE
:app:zipalignWeloveRelease UP-TO-DATE
:app:assembleWeloveRelease

BUILD SUCCESSFUL

Total time: 46.918 secs

对比上图可以体会gradle打包的过程!
0x06 Gradle Demo
==
放出我们项目工程的gradle,删除部分关键信息,给大家做个参考,如下:

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "***"
        minSdkVersion 10
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }

    // 配置keystore签名
    signingConfigs {
        releaseConfig {
            storeFile file('release-key.keystore')
            keyAlias "releasekey"
            storePassword "Replace Valid Store Password"
            keyPassword "Replace Valid Key Password"
        }
    }

    buildTypes {
        debug {

        }

        release {
            minifyEnabled true  // 进行混淆
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.releaseConfig
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    // 平台定制
    productFlavors {
        welove {}
    }

    lintOptions {
        ignoreWarnings true // 只报告错误
        checkReleaseBuilds true    // release构建时issus的严重级为fatal,若发现了致命(fatal)的问题,则中止构建
        abortOnError false  // 当lint发现错误时停止 gradle构建
    }

    // 通过查看打包顺序发现打welove的Release包时,第三步就是执行checkWeloveReleaseManifest
    // 而且在打Debug包时不会执行checkWeloveReleaseManifest,为了自动执行releaseCheck 增加以下代码
    tasks.whenTaskAdded { task ->
        if (task.name == 'checkWeloveReleaseManifest') {
            task.dependsOn releaseCheck
        }
    }
}

dependencies {
    // 编译libs目录下的所有jar包
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:support-v4:22.0.0'
    compile 'com.android.support:appcompat-v7:22.0.0'
    compile 'com.android.support:recyclerview-v7:22.0.0'
    compile 'com.android.support:multidex:1.0.1'
    compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
        transitive = true
    }
}

import java.util.regex.Pattern
task releaseCheck <<{
    // 检查 baidu api key 是不是有效值
    def baiduApiFile = file("$workspace/tools/WeloveConstants.java")
    def baiduApiFilePattern = Pattern.compile("\\s*public static final String BAIDU_MAP_API_KEY = ResourceUtil.getStr\\(R.string.(.*)\\)")
    def baiduApiFileMatcher = baiduApiFilePattern.matcher(baiduApiFile.getText())
    if (baiduApiFileMatcher.find()){
        def baiduApiUrl = baiduApiFileMatcher.group(1)
        if (!baiduApiUrl.equals("str_baidu_map_api_key")){
            throw new GradleException(baiduApiFile.toString() + " Contain Invalid Baidu API Key ")
        }
    }else {
        throw new GradleException(baiduApiFile.toString() + " Contain Invalid Baidu API Key ")
    }
}

因为使用crashlytics,所以根目录的build.gradle文件也贴出来:

buildscript {
    repositories {
        jcenter()
        maven { url 'https://maven.fabric.io/public' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.5.0'
        classpath 'io.fabric.tools:gradle:1.+'
    }
}

allprojects {
    repositories {
        jcenter()
        maven { url 'https://maven.fabric.io/public' }
    }
}

0x07 参考资料

Gradle 官方文档翻译: 文档一文档二
Android Gradle插件: 文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,116评论 25 707
  • 参考资料:http://gold.xitu.io/post/580c85768ac247005b5472f9htt...
    zhaoyubetter阅读 10,996评论 0 6
  • 第四章 山上论神功 这一坐便坐到了子夜,“哈哈哈,原来是这样一个‘无为’!哈哈哈”!楚越阳...
    起雾了么阅读 266评论 0 1
  • 诗歌天生不能生活在喧嚣之中, 不能生长在过分现代的大生活中 , 诗歌是丝绸,散文是棉布, 有的时候我们热爱丝绸的抚...
    迪ba阅读 224评论 0 0
  • 《北风》中,我听出两个字——初心 文¦钱黎洁 闺蜜呀,同学呀,朋友等,同龄人的孩子这几年陆续要么高考,要么出国,朋...
    钱钱show生活阅读 697评论 0 1