Android程序员代码改变世界

Gradle总结

2016-01-22  本文已影响1033人  坚坚老师

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插件: 文档

上一篇下一篇

猜你喜欢

热点阅读