0%

Android项目构建Gradle专栏——build基础

公众号地址:https://mp.weixin.qq.com/s/WkwgKyAachnfCxKw3b1BMg


1. 构建体系

Android 构建系统会编译应用资源和源代码,然后将它们打包成可供您测试、部署、签署和分发的 APK。
也就是说 构建: 从代码和应用资源到APK的过程
Android 采用的构建系统是 Gradle 工具, Android 项目架构中的一些和构建相关的文件就是供Gradle构建核心文件

  • build.gradle
  • settings.gradle
  • app/build.gradle
  • app/build.gradle

通过这些文件执行一系列的 Gradle Task 就完成从源代码和资源文件 —-> APK
参考下面的 project 目录, 项目中会有多个 module 每个module 内也都有 build.gradle 来负责构建事宜

2. Build流程

  • 从需要构建的模块开始, 确定依赖项
  • 编译
  • 打包
  • *编译器**: 源代码 –> dex 文件
  • *打包**: 将dex和编译后的资源 组合为 apk 文件 并且使用相应的密钥库来进行签名
    生成最终 APK 前,  zipalign 会对APK进行一定优化, 减少内存占用

3. 工具

gradle 构建都是通过 gradle 构建工具包实现的
可以到 sdk目录: Library/Android/sdk/版本/build-tools 下找到对应的工具

构建过程就是通过gradle脚本使用这些工具, 完成前面说的 编译和打包和优化

图片引用

图片里面很清楚的说明了构建过程的各个核心流程


  • aapt (Android asset package tool) :负责将xml文件和其他一些资源文件res … 编译打包应用资源形成供代码访问的一个索引表
  • aidl: 处理使用AIDL规范写的aidl文件, 编译为 java 源代码
  • javac: java –> class 文件
  • d8: class 文件 –> dex 文件
  • zipalign: 字节码对齐工具, 进行优化, 类似垃圾回收标记整理的过程
  • jarsigner: 使用密钥证书 为 apk 签名

对以上构建工具建议查阅官方资料: https://developer.android.com/studio/command-line?hl=zh-cn
里面都有详细权威介绍

4. build文件分析

settings.gradle

settings.gradle 文件位于项目的根目录下,用于指示 Gradle 在构建应用时应将哪些模块包含在内。
比如开源项目bytex settings.gradle 指明了哪些模块参与构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//opensource
include ':shrink-r-plugin'
include ':access-inline-plugin'
include ':getter-setter-inline-plugin'
include ':refer-check-plugin'
include ':closeable-check-plugin'
include ':serialization-check-plugin'
include ':const-inline-plugin'
include ':field-assign-opt-plugin'
include ':method-call-opt-plugin'
include ':SourceFileKiller'
include ':butterknife-check-plugin'

include ':coverage-plugin'
project(':coverage-plugin').projectDir = new File('coverage/coverage-plugin')
include ':coverage-lib'
project(':coverage-lib').projectDir = new File('coverage/coverage-lib')

project/build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* The buildscript block is where you configure the repositories and
* dependencies for Gradle itself—meaning, you should not include dependencies
* for your modules here. For example, this block includes the Android plugin for
* Gradle as a dependency because it provides the additional instructions Gradle
* needs to build Android app modules.
*/

buildscript {

/**
* The repositories block configures the repositories Gradle uses to
* search or download the dependencies. Gradle pre-configures support for remote
* repositories such as JCenter, Maven Central, and Ivy. You can also use local
* repositories or define your own remote repositories. The code below defines
* JCenter as the repository Gradle should use to look for its dependencies.
*
* New projects created using Android Studio 3.0 and higher also include
* Google's Maven repository.
*/

repositories {
google()
jcenter()
}

/**
* The dependencies block configures the dependencies Gradle needs to use
* to build your project. The following line adds Android plugin for Gradle
* version 4.1.0 as a classpath dependency.
*/

dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
}
}

/**
* The allprojects block is where you configure the repositories and
* dependencies used by all modules in your project, such as third-party plugins
* or libraries. However, you should configure module-specific dependencies in
* each module-level build.gradle file. For new projects, Android Studio
* includes JCenter and Google's Maven repository by default, but it does not
* configure any dependencies (unless you select a template that requires some).
*/

allprojects {
repositories {
google()
jcenter()
}
}
  • repositories: 指明依赖项目从哪里来, 从哪个仓库导入进来
  • dependencies: 指明具体的 groupID 和 ArtifactID 用来找到具体依赖库
  • buildScript: 这里的依赖是 Gradle 构建时候需要用的, 比如依赖一些库,需要使用他们定义的Gradle插件
    重点注意 buildScript是 Gradle 需要的库, 比如一些 gradle plugin

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
buildscript {

repositories {
maven { url 'https://jitpack.io' }
maven {
url uri('../gradle_plugins')
}
google()
jcenter()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "com.android.tools.build:gradle:$gradle_version"

//依赖了 com.github.dcendents:android-maven-gradle-plugin 是因为gradle构建阶段需要使用到其内的插件
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
}
}
1
2
// 因为在 buildScript 中 , classpath 依赖了库, 所以可以在项目中可以使用 该 plugin
apply plugin: 'com.github.dcendents.android-maven'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
buildscript {...}

allprojects {...}

// This block encapsulates custom properties and makes them available to all
// modules in the project.
ext {
// The following are only a few examples of the types of properties you can define.
compileSdkVersion = 28
// You can also create properties to specify versions for dependencies.
// Having consistent versions between modules can avoid conflicts with behavior.
supportLibVersion = "28.0.0"
...
}
...

全局 build.gradle 中的 ext 里面定义的一些 变量是全局可用的, 可以在其他模块中访问如:

1
2
3
4
5
6
7
8
9
10
11
android {
// Use the following syntax to access properties you defined at the project level:
// rootProject.ext.property_name
compileSdkVersion rootProject.ext.compileSdkVersion
...
}
...
dependencies {
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
...
}

module/build.gradle

模块下的 build.gradle 配置则是 对该模块生效的一些特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* The first line in the build configuration applies the Android plugin for
* Gradle to this build and makes the android block available to specify
* Android-specific build options.
*/

apply plugin: 'com.android.application'

/**
* The android block is where you configure all your Android-specific
* build options.
*/

android {

/**
* compileSdkVersion specifies the Android API level Gradle should use to
* compile your app. This means your app can use the API features included in
* this API level and lower.
*/

compileSdkVersion 28

/**
* buildToolsVersion specifies the version of the SDK build tools, command-line
* utilities, and compiler that Gradle should use to build your app. You need to
* download the build tools using the SDK Manager.
*
* This property is optional because the plugin uses a recommended version of
* the build tools by default.
*/

buildToolsVersion "29.0.2"

/**
* The defaultConfig block encapsulates default settings and entries for all
* build variants, and can override some attributes in main/AndroidManifest.xml
* dynamically from the build system. You can configure product flavors to override
* these values for different versions of your app.
*/

defaultConfig {

/**
* applicationId uniquely identifies the package for publishing.
* However, your source code should still reference the package name
* defined by the package attribute in the main/AndroidManifest.xml file.
*/

applicationId 'com.example.myapp'

// Defines the minimum API level required to run the app.
minSdkVersion 15

// Specifies the API level used to test the app.
targetSdkVersion 28

// Defines the version number of your app.
versionCode 1

// Defines a user-friendly version name for your app.
versionName "1.0"
}

/**
* The buildTypes block is where you can configure multiple build types.
* By default, the build system defines two build types: debug and release. The
* debug build type is not explicitly shown in the default build configuration,
* but it includes debugging tools and is signed with the debug key. The release
* build type applies Proguard settings and is not signed by default.
*/

buildTypes {

/**
* By default, Android Studio configures the release build type to enable code
* shrinking, using minifyEnabled, and specifies the default Proguard rules file.
*/

release {
minifyEnabled true // Enables code shrinking for the release build type.
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

/**
* The productFlavors block is where you can configure multiple product flavors.
* This allows you to create different versions of your app that can
* override the defaultConfig block with their own settings. Product flavors
* are optional, and the build system does not create them by default.
*
* This example creates a free and paid product flavor. Each product flavor
* then specifies its own application ID, so that they can exist on the Google
* Play Store, or an Android device, simultaneously.
*
* If you declare product flavors, you must also declare flavor dimensions
* and assign each flavor to a flavor dimension.
*/

flavorDimensions "tier"
productFlavors {
free {
dimension "tier"
applicationId 'com.example.myapp.free'
}

paid {
dimension "tier"
applicationId 'com.example.myapp.paid'
}
}

/**
* The splits block is where you can configure different APK builds that
* each contain only code and resources for a supported screen density or
* ABI. You'll also need to configure your build so that each APK has a
* different versionCode.
*/

splits {
// Settings to build multiple APKs based on screen density.
density {

// Enable or disable building multiple APKs.
enable false

// Exclude these densities when building multiple APKs.
exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
}
}
}

/**
* The dependencies block in the module-level build configuration file
* specifies dependencies required to build only the module itself.
* To learn more, go to Add build dependencies.
*/

dependencies {
implementation project(":lib")
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
  • compileSdkVersion 28 用来编译的 Android API 版本
  • buildToolsVersion “29.0.2” 用来 build 构建的 build-tools 版本

local.properties

系统本地环境属性

gradle.properties

配置一些全局的配置, 和 ext 闭包一样 可以在其他gradle文件中获得其中配置

5. gradle Task

gradle 执行的是一个个 task
gradle 开发的自定义plugin 也就是重写 apply 方法部分

每一次 Android 项目构建执行会执行一系列的gradle task 来完成整个构建过程比如:

可以参考该文章: https://mp.weixin.qq.com/s/6Cb6MqV9GQG60hBltss61A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Starting Gradle Daemon...
Gradle Daemon started in 902 ms
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:checkDebugAarMetadata UP-TO-DATE
> Task :app:generateDebugResValues UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:createDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksDebug UP-TO-DATE
> Task :app:processDebugMainManifest
> Task :app:processDebugManifest
> Task :app:javaPreCompileDebug UP-TO-DATE
> Task :app:mergeDebugNativeDebugMetadata NO-SOURCE
> Task :app:mergeDebugShaders UP-TO-DATE
> Task :app:compileDebugShaders NO-SOURCE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets UP-TO-DATE
> Task :app:compressDebugAssets UP-TO-DATE
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:mergeDebugJniLibFolders UP-TO-DATE
> Task :app:mergeDebugNativeLibs UP-TO-DATE
> Task :app:stripDebugDebugSymbols NO-SOURCE
> Task :app:validateSigningDebug UP-TO-DATE
> Task :app:mergeLibDexDebug
> Task :app:processDebugManifestForPackage
> Task :app:processDebugResources
> Task :app:compileDebugKotlin UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:compileDebugSources UP-TO-DATE
> Task :app:mergeDebugJavaResource UP-TO-DATE
> Task :app:dexBuilderDebug
> Task :app:mergeExtDexDebug
> Task :app:mergeProjectDexDebug
> Task :app:packageDebug
> Task :app:assembleDebug

BUILD SUCCESSFUL in 22s
27 actionable tasks: 10 executed, 17 up-to-date
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//aidl 转换aidl文件为java文件
> Task :app:compileDebugAidl

//生成BuildConfig文件
> Task :app:generateDebugBuildConfig

//获取gradle中配置的资源文件
> Task :app:generateDebugResValues

// merge资源文件
> Task :app:mergeDebugResources

// merge assets文件
> Task :app:mergeDebugAssets
> Task :app:compressDebugAssets

// merge所有的manifest文件
> Task :app:processDebugManifest

//AAPT 生成R文件
> Task :app:processDebugResources

//编译kotlin文件
> Task :app:compileDebugKotlin

//javac 编译java文件
> Task :app:compileDebugJavaWithJavac

//转换class文件为dex文件
> Task :app:dexBuilderDebug

//打包成apk并签名
> Task :app:packageDebug

可以自己run apk 的时候看一下build 窗口中的一系列 gradle task执行过程

gradle task 是 gradle 流水线执行的节点

开发中 会针对一些 特定位置运行gradle task

比如很多 task 是有依赖关系的, 比如 你可以让 taskA , 依赖 taskB
也可以在buid结束时候添加一些 task
可以参考: https://blog.csdn.net/ccpat/article/details/89342198简单介绍了一些task

6. 依赖

开发中经常接触到就是处理库之间的依赖关系

常见的依赖方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apply plugin: 'com.android.application'

android { ... }

dependencies {
// Dependency on a local library module
implementation project(":mylibrary")

// Dependency on local binaries
implementation fileTree(dir: 'libs', include: ['*.jar'])

// Dependency on a remote binary
implementation 'com.example.android:app-magic:12.3'
}

里面代表了三种依赖源
本地模块、 jar包、 远程maven仓库


除了依赖源头, 依赖的方式也很多:

  • implementation
  • api
  • compileOnly
  • runtimeOnly
  • annotationProcessor

Android项目构建gradle专栏系列持续更新中……

参考:
https://developer.android.google.cn/studio/build/index.html
https://mp.weixin.qq.com/s/6Cb6MqV9GQG60hBltss61A
https://cloud.tencent.com/developer/article/1392511
https://www.jianshu.com/p/e73510605c56
https://developer.android.com/studio/build?hl=zh-cn

微信扫码关注公众号,追踪更多博文