本文整理自: http://stormzhang.com/devtools/2014/12/18/android-studio-tutorial4/ http://stormzhang.com/devtools/2015/01/05/android-studio-tutorial5/ http://stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/

什么是Gradle?


Gradle是一种依赖管理工具,基于Groovy语言,面向Java应用为主,它抛弃了基于XML的各种繁琐配置,取而代之的是一种基于Groovy的内部领域特定(DSL)语言。

Gradle基本概念


项目目录:

gradle打包java项目可执行的 jar_配置文件

从上到下依次分析

1. GradleDemo/app/build.gradle

这个文件是app文件夹下这个Module的gradle配置文件,也可以算是整个项目最主要的gradle配置文件,我们来看下这个文件的内容:

//声明是android程序
apply plugin: 'com.android.application'

android {
    //SDK版本
    compileSdkVersion 25
    //build tools版本
    buildToolsVersion "25.0.2"
    defaultConfig {
        // 应用包名
        applicationId "com.standards.gradledemo"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        debug{
            //debug版本
        }
        release {
            //是否进行混淆
            minifyEnabled false
            //混淆文件配置
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    // 编译libs目录下的所有jar包
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    testCompile 'junit:junit:4.12'
    // 编译devlib模块
    compile project(':devlib')
}

说明:

  • buildToolsVersion这个需要你本地安装该版本才行,很多人导入新的第三方库,失败的原因之一是build version的版本不对,这个可以手动更改成你本地已有的版本或者打开 SDK Manager 去下载对应版本。
  • proguardFiles这部分有两段,前一部分代表系统默认的Android程序的混淆文件,该文件已经包含了基本的混淆声明,免去了我们很多事,这个文件的目录在 /tools/proguard/proguard-Android.txt , 后一部分是我们项目里的自定义的混淆文件,目录就在 app/proguard-rules.txt , 如果你用Studio 1.0创建的新项目默认生成的文件名是 proguard-rules.pro , 这个名字没关系,在这个文件里你可以声明一些第三方依赖的一些混淆规则,由于是开源项目,这里未进行混淆。最终混淆的结果是这两部分文件共同作用的。
  • 以上文件里的内容只是基本配置,其实还有很多自定义部分,如自动打包debug,release,beta等环境,签名,多渠道打包等,后续会单独拿出来讲解。

2. GradleDemo/devlib/build.gradle

每一个Module都需要有一个gradle配置文件,语法都是一样,唯一不同的是开头声明的是 apply plugin: ‘com.android.library’

3. GradleDemo/gradle

这个目录下有个 wrapper 文件夹,里面可以看到有两个文件,我们主要看下 gradle-wrapper.properties 这个文件的内容:

#Thu Jul 06 16:20:36 CST 2017
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

可以看到里面声明了gradle的目录与下载路径以及当前项目使用的gradle版本,这些默认的路径我们一般不会更改的,这个文件里指明的gradle版本不对也是很多导包不成功的原因之一。

4. GradleDemo/build.gradle

这个文件是整个项目的gradle基础配置文件,我们来看看这里面的内容

// Top-level build file where you can add configuration options common to all sub-projects/modules.

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

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

    allprojects {
        repositories {
            jcenter()
        }
    }

    task clean(type: Delete) {
        delete rootProject.buildDir
    }

内容主要包含了两个方面:一个是声明仓库的源,这里可以看到是指明的jcenter(), 之前版本则是mavenCentral(), jcenter可以理解成是一个新的中央远程仓库,兼容maven中心仓库,而且性能更优。另一个是声明了android gradle plugin的版本,android studio 1.0正式版必须要求支持gradle plugin 1.0的版本。

5. GradleDemo/settings.gradle

这个文件是全局的项目配置文件,里面主要声明一些需要加入gradle的module,我们来看看9GAG该文件的内容:

include ':app', ':devlib'

文件中的 app, devlib 都是module,如果还有其他module都需要按照如上格式加进去。也可以Ctrl+Shift+Alt+S 进入项目配置里面可视化配置。

Gradle命令详解


命令行Gradle编译的过程

1、切换到GradleDemo项目的根目录,执行 ./gradlew -v 来查看下项目所用的Gradle版本 如果你是第一次执行会去下载Gradle,下载成功后会看到如下信息

------------------------------------------------------------
Gradle 2.14.1
------------------------------------------------------------

Build time:   2016-07-18 06:38:37 UTC
Revision:     d9e2113d9fb05a5caabba61798bdb8dfdca83719

Groovy:       2.4.4
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_25 (Oracle Corporation 25.25-b02)
OS:           Windows 7 6.1 amd64

2、接着执行 ./gradlew clean执行这个命令去下载Gradle的一些依赖,下载成功并编译通过时会看到如下信息:

:clean UP-TO-DATE
:app:clean
:devlib:clean                 

BUILD SUCCESSFUL

紧接着在 GradleDemo/app/build/outputs/apk 目录下会看到类似于app-debug-unaligned.apk, app-release-unsigned.apk等,看名字应该能理解意思,unaligned代表没有进行zip优化的,unsigned代表没有签名的。然后就可以直接安装apk查看运行效果了。

Gradle常用命令

上面大家接触了一些命令如 ./gradlew -v ./gradlew clean ./gradlew build, 这里注意是./gradlew, ./代表当前目录,gradlew代表 gradle wrapper,意思是gradle的一层包装,大家可以理解为在这个项目本地就封装了gradle,即gradle wrapper, 在GraldeDemo/gradle/wrapper/gralde-wrapper.properties文件中声明了它指向的目录和版本。只要下载成功即可用grdlew wrapper的命令代替全局的gradle命令。

理解了gradle wrapper的概念,下面一些常用命令也就容易理解了。

./gradlew -v 版本号 ./gradlew clean 清除9GAG/app目录下的build文件夹 ./gradlew build 检查依赖并编译打包 这里注意的是 ./gradlew build 命令把debug、release环境的包都打出来,如果正式发布只需要打Release的包,该怎么办呢,下面介绍一个很有用的命令 assemble, 如

./gradlew assembleDebug 编译并打Debug包 ./gradlew assembleRelease 编译并打Release的包 除此之外,assemble还可以和productFlavors结合使用,具体在下一篇多渠道打包进一步解释。

./gradlew installRelease Release模式打包并安装 ./gradlew uninstallRelease 卸载Release模式包

Gradle多渠道打包


友盟多渠道打包

1.以友盟统计为例,在AndroidManifest.xml里面会有这么一段:

<meta-data
         android:name="UMENG_CHANNEL"
         android:value="Channel_ID" />

里面的Channel_ID就是渠道标示。我们的目标就是在编译的时候这个值能够自动变化。

第一步,在AndroidManifest.xml里配置PlaceHolder

<meta-data
        android:name="UMENG_CHANNEL"
        android:value="${UMENG_CHANNEL_VALUE}" />

第二步,在build.gradle设置productFlavors

android {  
        productFlavors {
            xiaomi {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
            }
            _360 {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "_360"]
            }
            baidu {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
            }
            wandoujia {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
            }
        }  
    }
或者批量修改

    android {  
        productFlavors {
            xiaomi {}
            _360 {}
            baidu {}
            wandoujia {}
        }  

        productFlavors.all { 
            flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] 
        }
    }

很简单清晰有没有?直接执行 ./gradlew assembleRelease , 然后就可以静静的喝杯咖啡等待打包完成吧。

assemble结合Build Variants来创建task

  • ./gradlew assembleDebug
  • ./gradlew assembleRelease

除此之外 assemble 还能和 Product Flavor 结合创建新的任务,其实 assemble 是和 Build Variants 一起结合使用的,而 Build Variants = Build Type + Product Flavor , 举个例子大家就明白了:

如果我们想打包wandoujia渠道的release版本,执行如下命令就好了:

  • ./gradlew assembleWandoujiaRelease 如果我们只打wandoujia渠道版本,则:
  • ./gradlew assembleWandoujia 此命令会生成wandoujia渠道的Release和Debug版本

同理我想打全部Release版本:

  • ./gradlew assembleRelease 这条命令会把Product Flavor下的所有渠道的Release版本都打出来。

总之,assemble 命令创建task有如下用法:

  • assemble: 允许直接构建一个Variant版本,例如assembleFlavor1Debug。
  • assemble: 允许构建指定Build Type的所有APK,例如assembleDebug将会构建Flavor1Debug和Flavor2Debug两个Variant版本。
  • assemble: 允许构建指定flavor的所有APK,例如assembleFlavor1将会构建Flavor1Debug和Flavor1Release两个Variant版本。

完整的gradle脚本

apply plugin: 'com.android.application'

    def releaseTime() {
        return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
    }

    android {
        compileSdkVersion 21
        buildToolsVersion '21.1.2'

        defaultConfig {
            applicationId "com.boohee.*"
            minSdkVersion 14
            targetSdkVersion 21
            versionCode 1
            versionName "1.0"

            // dex突破65535的限制
            multiDexEnabled true
            // 默认是umeng的渠道
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
        }

        lintOptions {
            abortOnError false
        }

        signingConfigs {
            debug {
                // No debug config
            }

            release {
                storeFile file("../yourapp.keystore")
                storePassword "your password"
                keyAlias "your alias"
                keyPassword "your password"
            }
        }

        buildTypes {
            debug {
                // 显示Log
                buildConfigField "boolean", "LOG_DEBUG", "true"

                versionNameSuffix "-debug"
                minifyEnabled false
                zipAlignEnabled false
                shrinkResources false
                signingConfig signingConfigs.debug
            }

            release {
                // 不显示Log
                buildConfigField "boolean", "LOG_DEBUG", "false"

                minifyEnabled true
                zipAlignEnabled true
                // 移除无用的resource文件
                shrinkResources true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.release

                applicationVariants.all { variant ->
                    variant.outputs.each { output ->
                        def outputFile = output.outputFile
                        if (outputFile != null && outputFile.name.endsWith('.apk')) {
                            // 输出apk名称为boohee_v1.0_2015-01-15_wandoujia.apk
                            def fileName = "boohee_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
                            output.outputFile = new File(outputFile.parent, fileName)
                        }
                    }
                }
            }
        }

        // 友盟多渠道打包
        productFlavors {
            wandoujia {}
            _360 {}
            baidu {}
            xiaomi {}
            tencent {}
            taobao {}
            ...
        }

        productFlavors.all { flavor ->
            flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }
    }

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:support-v4:21.0.3'
        compile 'com.jakewharton:butterknife:6.0.0'
        ...
    }