Android开发必备的Gradle知识

前言

本篇文章来自本人以前的学习笔记,本来笔记语言比较生硬,之前有同事问我关于Gradle的知识,对我的笔记评价还不错。于是趁最近有时间对语言稍加润色,发出来供大家参考。这篇笔记来自《巧用Gradle构建Android应用》(《Gradle Recipes for Android》)这本书(以前从学校图书馆借的书,怕忘记一些知识点,所以做了笔记)。

目录

1. 基础

Android Studio使用Gradle做构建、打包和部署。采用脚本语言Groovy。

1.1 认识Gradle构建文件
- 示例1-1 setting.gradle 文件里面
include ':app'
include语句表示app子目录是唯一的子项目。如果你添加一个Android库项目,其也会被添加到这个文件。
- 示例1-2 build.gradle文件顶层。

// Top-level build file where you can add configuration options common to all sub-projects/modules.
//你可以在构建文件顶层添加所有子项目或模块通用的配置选项

buildscript {
    repositories {
        google()
        jcenter()
        maven { url 'https://maven.google.com' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        //注意:不要把你的应用程序依赖放在这里,它们属于各独立模块的build.gradle文件
    }
}

allprojects {
    repositories {
        jcenter()
        google()

        maven {
            credentials {
                username 'username'
                password 'password'
            }
            url 'http://repo.mycompany.com/maven2'
        }
    }
}

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

Gradle本身默认不包含Android的功能,Google为Gradle提供了一个Android的插件,其简化了Android项目的配置。构建文件顶层的buildscript块告诉Gradle去哪下载这个插件。
allprojects段顶层项目和所有子项目都默认使用 jcenter() 仓库来下载所有的Java第三方依赖库。
gradle允许你自定义你的task,并插入到有向无环图(DAG)(例如task clean方法)。
- 示例1-3 app的build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    buildToolsVersion "26.0.2"

    defaultConfig {
        applicationId "com.kousenit.myandroidapp"
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.0.2'
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    testImplementation 'junit:junit:4.12'
}

fileTreey依赖意味着libs目录下所有以.jar结尾的文件都被添加到了编译类路径。
implementation 'com.android.support:appcompat-v7:27.0.2'意味着Gradle从Android支持的库中添加版本为27.0.2的appcompat-v7 jar文件。注意-v7意味着支持Android应用程序Android 7及以后版本,并不是库本身的版本7这个库被定义为编译时依赖,因此其所有的类在整个项目都可用。
顶层Android构建文件通过buildscript块将Android的Gradle插件添加到了你的项目中。模块的构建文件通过“apply”应用这个插件,因此将Android块添加到Gradle的DSL(领域特定语言)中。Android项目使用com.android.application插件。
1.2 认识SDK版本以及其他的默认值

compileSdkVersion指定目标编译环境
buildToolsVersion指定构建工具版本
applicationId应用程序的“package”名字
minSdkVersion应用程序支持的最小Android SDK版本
targetSdkVersion应用程序目标的Android版本
versionCode数值代表你的app相对于其他的版本(应用升级时候用)
versionName代表app发行版本,面向用户的
compileOptions这个app期望使用JDK1.7

1.3 运行Gradle的方法(
Linux(./gradlew),Windows(gradlew))
查看gradle可以使用的任务:

E:\work\gradletest>gradlew task
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :tasks 

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleArro - Assembles all Arro builds.
assembleDebug - Assembles all Debug builds.
assembleFriendly - Assembles all Friendly builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.
cleanBuildCache - Deletes the build cache directory.
compileArroDebugAndroidTestSources
compileArroDebugSources
compileArroDebugUnitTestSources
compileArroReleaseSources
compileArroReleaseUnitTestSources
compileFriendlyDebugAndroidTestSources
compileFriendlyDebugSources
compileFriendlyDebugUnitTestSources
compileFriendlyReleaseSources
compileFriendlyReleaseUnitTestSources
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'gradletest'.
components - Displays the components produced by root project 'gradletest'. [incubating]
dependencies - Displays all dependencies declared in root project 'gradletest'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradletest'.
dependentComponents - Displays the dependent components of components in root project 'gradletest'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'gradletest'. [incubating]
projects - Displays the sub-projects of root project 'gradletest'.
properties - Displays the properties of root project 'gradletest'.
tasks - Displays the tasks runnable from root project 'gradletest' (some of the displayed tasks may belong to subprojects).

Install tasks
-------------
installArroDebug - Installs the DebugArro build.
installArroDebugAndroidTest - Installs the android (on device) tests for the ArroDebug build.
installArroRelease - Installs the ReleaseArro build.
installFriendlyDebug - Installs the DebugFriendly build.
installFriendlyDebugAndroidTest - Installs the android (on device) tests for the FriendlyDebug build.
installFriendlyRelease - Installs the ReleaseFriendly build.
uninstallAll - Uninstall all applications.
uninstallArroDebug - Uninstalls the DebugArro build.
uninstallArroDebugAndroidTest - Uninstalls the android (on device) tests for the ArroDebug build.
uninstallArroRelease - Uninstalls the ReleaseArro build.
uninstallFriendlyDebug - Uninstalls the DebugFriendly build.
uninstallFriendlyDebugAndroidTest - Uninstalls the android (on device) tests for the FriendlyDebug build.
uninstallFriendlyRelease - Uninstalls the ReleaseFriendly build.

Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices.
connectedArroDebugAndroidTest - Installs and runs the tests for arroDebug on connected devices.
connectedCheck - Runs all device checks on currently connected devices.
connectedFriendlyDebugAndroidTest - Installs and runs the tests for friendlyDebug on connected devices.
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintArroDebug - Runs lint on the ArroDebug build.
lintArroRelease - Runs lint on the ArroRelease build.
lintFriendlyDebug - Runs lint on the FriendlyDebug build.
lintFriendlyRelease - Runs lint on the FriendlyRelease build.
lintVitalArroRelease - Runs lint on just the fatal issues in the arroRelease build.
lintVitalFriendlyRelease - Runs lint on just the fatal issues in the friendlyRelease build.
test - Run unit tests for all variants.
testArroDebugUnitTest - Run unit tests for the arroDebug build.
testArroReleaseUnitTest - Run unit tests for the arroRelease build.
testFriendlyDebugUnitTest - Run unit tests for the friendlyDebug build.
testFriendlyReleaseUnitTest - Run unit tests for the friendlyRelease build.

To see all tasks and more detail, run gradlew tasks --all

To see more detail about a task, run gradlew help --task <task>


BUILD SUCCESSFUL in 26s
1 actionable task: 1 executed

1.4 添加Java库的依赖
gradle依赖的完整语法testImplementation group:'junit',name:'junit',version:'4.12' 依赖的快捷语法testImplementation 'junit:junit:4.12'
版本号以变量形式(不推荐)testImplementation 'junit:junit:4.+'
gradle默认遵循传递依赖,如果你想针对一个特定的库关掉这个功能,使用transitive标志。

implementation ('com.squareup.retrofit2:retrofit:2.4.0'){
        transitive = false
    }

如果你想要一个模块的jar,不引入任何额外的依赖,你也可以像这样指定

 implementation 'org.codehaus.groovy:groovy:3.0.0-alpha-3@jar'

你也可以从dependencies块中排除一个传递依赖

implementation ('org.spockframework:spock-core:1.2-RC1-groovy-2.5'){
        exclude group: 'org.codehaus.groovy'
        exclude group: 'junit'
    }

1.5 配置仓库
任何一个Maven仓库都可以使用maven并且跟随一个url块来添加到默认的仓库列表中去。

repositories {
        google()
        jcenter()
        maven{
            url 'http://repo.spring.io/milestone'
        }
    }

通过密码保护的仓库可以使用一个credentials 块。

maven{
            credentials{
                username 'username'
                password 'password'
            }
            url 'http://repo.spring.io/milestone'
        }

如果你有本地文件系统的文件,你可以通过flatDir 语法将一个文件目录作为一个仓库。

    repositories {
        google()
        jcenter()
        maven{
            credentials{
                username 'username'
                password 'password'
            }
            url 'http://repo.spring.io/milestone'
        }
        flatDir{
            dirs 'lib'
        }
    }

这是除了显式的使用 filesfileTree 添加文件到dependencies 块的另一种方式。

2. 项目属性

2.1 属性设置
ext属性(使用ext块配置通用的值)—-“ext”代表 “extra”。例如:

ext {
     AAVersion = '1.1.2'
}
implementation "com.android.support.constraint:constraint-layout:$AAVersion"

如果你想要将实际的值挪出构建文件(用户名,密码)
原代码:

 credentials{
                username 'username'
                password 'password'
            }

可以把它添加到项目根目录下的gradle.properties文件:

login = 'user'
pass = '123456'

然后

 credentials{
                username login
                password pass
            }

你还可以通过命令行设置这些值,通过使用-P传参给gradle。

gradlew -Plogin = me -Ppassword = password assembleDebug

使属性动态变化:

ext{
    if(!project.hasProperty('login'){
        login = 'login_from_build_file'
    }
    if(!project.hasProperty('pass'){
        pass = 'pass_from_build_file'
    }
}

这些也可以通过命令行来设置,并且拥有最高优先级。
2.2 在项目之间分享设置

 allprojects {
    repositories {
        jcenter()
    }
}

此段代码对于这个默认项目来说意味着顶层项目和app模块。
另一个做法是使用subproject块,例如多个库项目在自己的构建文件中应用library插件,可以通过在顶层应用这个插件来消除重复。

subprojects{
    apply plugin: `com.android.library`
}

2.3 为发布apk签名
默认情况下,调试用的秘钥存储库在你的用户目录下的.Android子目录下面。默认的名字叫做debug.keystore,并且其密码是Android。

3. 处理构建类型

一个构建类型决定app如何被打包。Gradle的Android插件默认支持两种不同类型的构建:debug和release。

  buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

    }

其中,minifyEnabled代表在打包app时自动删除无用的资源。如果设置成true,Gradle还会删除无需用到的依赖库。这只会在shrinkResources属性同时设置成true时生效。

  buildTypes {
        release {
        //打开代码压缩
            minifyEnabled true
            //打开资源压缩
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

    }

为了能够在一台机器上安装多种构建类型,Android必须能够分辨出它们的应用程序ID。applicationIDSuffix允许Gradle生成多个APK,每个都有自己的ID。

  buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
        }
        debug{
            applicationIdSuffix '.debug'
            versionNameSuffix '-debug'
        }
    }

现在发布版本和调试版本的app都可以被部署到同一台设备上了。
1. 产品定制和变种
如果我们想要构建同一个应用程序,但是使用不同的资源或类。这时要进行产品定制。
使用Android闭包的productFlavors块来声明一个产品定制。
假设一个”Hello,World”风格的Android app,基于简单的EditText输入名字来欢迎用户。你可以通过引入“friendly”,“arro”来给这个app一个态度。

    productFlavors{
        friendly{
            applicationId 'com.test.friendly'
        }
        arro{
            applicationId 'com.test.arro' +
                    ''
        }
    }

我们可以有多个定制。在这个例子中,每个定制拥有一个略微不同的applicationId,所以这些定制可以被安装到同一个设备上。
每个产品定制可以拥有其自己的值,如下面的属性,还有一些其他的基于默认的defaultConfig的相同属性。
- applicationId
- minSdkVersion
- targetSdkVersion
- versionCode
- versionName
- sigingConfig
每个定制定义了自己的源代码集和资源,其与主代码集是兄弟关系。在我们的示例中,意味着除了app/src/main/java以外,你还可以添加源文件在如下目录中:
- app/src/arro/java
- app/src/friendly/java
你还可以添加额外的资源文件到如下目录:
- app/src/arro/res
- app/src/friendly/res/layout
- app/src/friendly/res/values
所有任何res的子目录也是一样的。同样的资源目录结构也可以应用到所有的定制。
为了部署一个特定的变种,Android Studio提供了一个Build Variant视图。从下拉框中选择合适的变种,并像平常一样部署。
2. 合并资源
如果我们要在同一个Activity展示不同的文字,应该如何设置呢?其实所有的定制都有自己的资源目录,在appres下。在这里,一个叫做values的子目录被添加进去了,一个叫做strings.xml的文件拷贝从app/src/main/res/values拷贝进去了。arro的定制string.xml如下所示。

<resources>
    <string name="app_name">GradleTest</string>
    <string name="hello_world">I\'m
        Arro</string>
</resources>

通过合并项目定制和构建类型的res文件夹以及主要的目录树来合并资源。优先级是:构建类型覆盖产品定制,其覆盖main代码集。
在我们的MainActivity展示一段文字

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

再看看我们Friendly定制的Strings.xml

<resources>
    <string name="app_name">GradleTest</string>
    <string name="hello_world">I\'m Friendly</string>
</resources>

运行之后就可以显示不同定制的文字了。
3. 定制维度
一个产品定制还不够,你需要另一个标准类区分你应用程序的不同版本。

//添加定制维度 attitude和client
 defaultConfig {
        applicationId "com.example.tangx.gradletest"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        flavorDimensions "attitude", "client"
//        flavorDimensions "versionCode"
    }
    productFlavors {
        arrogant {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.arrg'
        }
        friendly {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.frnd'
        }
        obsequious {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.obsq'
        }
        stark {
            dimension 'client'
        }
        wayne {
            dimension 'client'
        }
    }

此处有两个维度定制:attitude和client。arrogant、friendly和obsequious定制在attitude维度上的,stark和wayne是client类型的定制。
这个组合会生成很多变种。如(obsequious)下:
- obsequiousStarkDebug
- obsequiousStarkRelease
- obsequiousWayneDebug
- obsequiouswayneRelease
两种构建类型,三种态度的定制和两种客户的定制,一共产生2 * 3 * 2=12不同的变种。
4. 合并不同定制间的java源代码
如果想要为单独的产品定制添加Android activities或者其他java类,可以用此方法。
虽然定制和构建类型中的字符串和布局资源与main源码集中的值会相应的覆盖,java类却又不同。如果main源码集中的代码引用了一个特定的类每个定制和构建类型可以有自己的实现,只要在main中没有就行。

4. 使用Android库

从Gradle的视角,Android库就是根项目的子项目。意味着它们就像Android应用程序一样,但是在一个子目录中。被添加的模块的名字因此被添加到setting.gradle文件中。
例如一个添加了模块的settings.gradle文件
include ':app',':icndb'
在这个例子中,Android库叫做icndb。
每个库都有自己的Gradle构建文件,其支持与根项目相同的设置。你可以指定最小和目标SDK版本、自定义构建类型、添加构建定制,并且修改依赖。重要的区别是Gradle构建使用了不同的插件。

//使用library插件
apply plugin: 'com.android.library'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    packagingOptions{
    //从多个依赖中移除冲突文件
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/license.txt'
        exclude 'LICENSE.txt'
    }

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
}

在app编译时使用icndb库

dependencies {
    compile project(':icndb')
}

构建过程本身生成调试和发布版本的库,在icndb/build/outputs/aar目录下。

5. 提升Gradle构建性能
  1. Gradle守护进程
    Gradle守护进程实在构建之间也持续运行的后台进程,同时缓存数据和代码。为确保守护进程启动了,在gradle properties中添加设置:
org.gradle.deamon = true

Gradle团队强烈推荐你不要在持续集成服务器上使用守护进程,在那里稳定和重复构建比性能更有价值。
2. 并行编译
Gradle有一个“incubating”选项来并行编译独立的项目。在gradle.properties文件中添加如下配置:

org.gradle.parallel = true

注意这里可能并没有用,大多数Android项目的模块都是相互关联的,使得并行编译并不能带来很多的好处。
3. 按需配置

org.gradle.configureondemand = true

大多数Android程序都只要很少数量的子项目,所以这个功能可能也不会很实用。
4. 修改JVM设置
最终Gradle构建会在一个Java进程中进行,所以影响JVM性能的因素也会影响Gradle的性能。如下演示Java虚拟机的一个方便的设置。

rg.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

-Xmx标志指定这个Java进程使用的最大内存数量。一个-Xmx标志指示了最初为这个进程分配的内存数量。这个例子还修改了“永久代”的空间大小,并且当java.lang.OutOfMemoryError发生时,将堆中的情况导出到一个文件中。
5. 使用dex选项
Android块允许你设置一个选项来控制转换java二进制代码(如.class文件)为Dalvik可执行文件(.dex文件)的“dex”进程。Android块内部的dexOptions块包含了如下选项:

 dexOptions{
        javaMaxHeapSize '2g'
        jumboMode = true
        preDexLibraries = true
    }

使用javaMaxHeapSize作为dx运行时最大堆大小指定Xmx值得一种替代方式,增量为1024m。所以这里设置为2g。
应用“jumbo模式”运载在dex文件中出现大量的字符串。如果这是个问题,你可能想要花更多的时间配置ProdGuard。
preDexLibraries会提前在库上运行dex进程,正如其所听起来的那样。如文档所说,“这可以提高增量构建性能,但是单纯的构建可能会更慢”
5. 剖析你的构建
你可以在命令行运行Gradle时加上–profile的选项来生成关于构建的有用信息。结果会在build/reports/profile目录下以HTML的形式被写入。命令如下:

gradlew --profile assembleDebug

多个标签将总结报告分成了几个独立的配置步骤,配置和执行。在这样大小的项目中这里看不到太多的东西,但是在大型项目中,这是一个很好的发现构建过程瓶颈的方式。
——“Gradle Recipes for Android by Ken Kousen”

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值