Android Studio 目录层级
├── app #Android App目录
│ ├── app.iml
│ ├── build #构建输出目录
│ ├── build.gradle #构建脚本
│ ├── libs #so相关库
│ ├── proguard-rules.pro #proguard混淆配置
│ └── src #源代码,资源等
├── build
│ └── intermediates
├── build.gradle #工程构建文件
├── gradle
│ └── wrapper
├── gradle.properties #gradle的配置 外部属性
├── gradlew #gradle wrapper linux shell脚本
├── gradlew.bat
├── LibSqlite.iml
├── local.properties #配置Androod SDK位置文件
└── settings.gradle #工程配置
settings.gradle
settings.gradle
用于配置 project。settings 文件声明了所需的配置来实例化项目的层次结构,标明其下有几个 module,比如这里包含一个:app
module。
include ':app'
根目录的 build.gradle
和
settings.gradle
在同一目录下的build.gradle
是一个顶级的 build 配置文件,在这里可以为所有的 project 以及 module 配置一些常用的配置。
根目录中的 build.gradle 默认包含 2 个代码块
-
buildscript{}
用于配置构建脚本所用到的代码库和依赖关系。这里定义了 Android 编译工具的类路径。repositories 中, jCenter 是一个著名的 Maven 仓库。 -
allprojects{}
用于定义所有模块需要用到的一些公共属性。这里定义的属性会被应用到所有的 module 中,但为了保证每个项目的独立性,一般不会在这里操作太多共有的东西
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()//使用 jcenter库
}
dependencies {
// 依赖 android提供的 1.1.0 的 gradle build
classpath 'com.android.tools.build:gradle:1.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
//为所有的工程的 repositories 配置为 jcenters
allprojects {
repositories {
jcenter()
}
}
模块级 build.gradle
除了更目录下的 build 文件,gradle 支持对每个 module 进行配置,主要有三个重要的代码块:plugin、android 和 dependencies
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "org.flysnow.demo"
minSdkVersion 9
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
}
apply plugin
第一行 apply plugin: 'com.android.application'
,这表示该 module 是一个 app module,应用了com.android.application 插件。
如果是一个 android library,那么这里的是 apply plugin: 'com.android.library'
-
apply plugin
:声明引用插件的类型。如果是库的话就加 -
apply from
:表示引用其他的配置文件,比如apply from:"config.gradle"
android
compileSdkVersion & buildToolsVersion
-
compileSdkVersion
: 基于哪个 SDK 编译,这里的 Version 是 API LEVEL,是21 -
buildToolsVersion
: 基于哪个构建工具版本进行构建的
defaultConfig
defaultConfig
是默认配置,如果没有其他的配置覆盖,就会使用这里的。
常用属性:
applicationId
配置包名的versionCode
版本号versionName
版本名称minSdkVersion
app能够运行的最小版本-
targetSdkVersion
目标设备sdk(向前兼容)一般
minSdkVersion
<targetSdkVersion
<=compileSdkVersion
miniSdkVersion
: app能运行的最小版本,比如minSdkVersion=18
(Android 4.3)那么在低于这个系统版本的设备上就会提示不能安装或运行。
compileSdkVersion
: 是我们告诉 Gradle,我们是用哪一版本的Android Sdk去编译程序的,可以使用这个版本的 API,比如我们使用的是 7.0 的版本
compileSdkVersion=24
,那么我们对于拍照裁剪图片等功能的操作,就可以使用 FileProvider了。
targetSdkVersion
: 系统通过 targetSdkVersion 来保证 Android 的向前兼容性,在 Android4.4 之后的设备上,系统会判断你的 targetSdkVersion 是否小于 19,如果小于的话,那就按照 19 之前的 api 方法,如果大于等于 19,那么就按照之后的 api 方法来走,保证了程序运行的一致性。也就是向前兼容性。
defaultConfig{} 还有很多其他的配置,后面介绍
buildTypes
buildTypes
是构建类型,常用的有 release
和 debug
两种,可以在这里面启用混淆,启用 zipAlign
以及配置签名信息等。
buildTypes{} 中的其他配置,后面介绍
dependencies
dependencies
就不属于 Android 专有的配置了,它定义了该 module 需要依赖的 jar,aar,jcenter库信息。
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'
}
本地项目依赖
implementation project(':mylibrary')
这声明了对一个名为“mylibrary”(此名称必须与在您的 settings.gradle
文件中使用 include:
定义的库名称相符)的 Android 库模块的依赖关系。在构建您的应用时,构建系统会编译该库模块,并将生成的编译内容打包到 APK 中。
本地二进制文件依赖项
implementation fileTree(dir: 'libs', include: ['*.jar'])
Gradle 声明了对项目的 module_name/libs/
目录中 JAR 文件的依赖关系(因为 Gradle 会读取 build.gradle
文件的相对路径)。
或者,您也可以按如下方式指定各个文件:
implementation files('libs/foo.jar', 'libs/bar.jar')
远程二进制文件依赖项
implementation 'com.example.android:app-magic:12.3'
这实际上是以下代码的简写形式:
implementation group: 'com.example.android', name: 'app-magic', version: '12.3'
这声明了对“com.example.android”命名空间组内的 12.3 版“app-magic”库的依赖关系。
注意:此类远程依赖项要求您声明适当的远程代码库,Gradle 应在其中查找相应的库。如果本地不存在相应的库,那么当 build 需要它时(例如,当您点击 Sync Project with Gradle Files 图标 [站外图片上传中...(image-aa6d99-1607933369919)] 或运行 build 时),Gradle 会从远程站点提取它。
依赖项配置
-
implementation
: Gradle 会将依赖项添加到编译类路径和构建输出,依赖不会暴露给其他模块 -
api
:Gradle 会将依赖项添加到编译类路径和构建输出,依赖会传递给其他依赖该模块的模块 -
compileOnly
:Gradle 只会将依赖项添加到编译类路径,仅在编译期依赖,运行时可有可无时可以使用该配置 -
runtimeOnly
:Gradle 只会将依赖项添加到构建输出,以便在运行时使用。也就是说,不会将其添加到编译类路径。
annotationProcessor
:添加注解处理器的库依赖关系,需使用该配置将其添加到注解处理器类路径。注:Kotlin 使用 kapt 声明注解处理器依赖项
以上配置可以将依赖项应用于所有构建变体
如果想为特定的构建变体源代码集或测试源代码集声明依赖,必须将配置的名称首字母大写,并在前面加上构建变体或测试源代码集的名称作为前缀
dependencies {
freeImplementation 'com.google.firebase:firebase-ads:9.8.0'
}
不过,如果您想为将产品变种和构建类型组合在一起的变体添加依赖项,就必须在 configurations
代码块中初始化配置名称。以下示例向“freeDebug”构建变体添加了 runtimeOnly
依赖项(使用本地二进制文件依赖项):
configurations {
// Initializes a placeholder for the freeDebugRuntimeOnly dependency
// configuration.
freeDebugRuntimeOnly {}
}
dependencies {
freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
}
ext{} 代码块 定制项目属性
在根目录 build.gradle 中, 可以定制适用于所有模块的属性,通过 ext
代码块来实现,如:
ext {
compileSdkVersion = 28
minSdkVersion = 18
}
然后在模块的 build.gradle 配置引用这些属性,语法为:
rootProject.ext.{属性名}
, 比如:
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
}
构建类型 android.buildTypes{}
默认情况下,Android Plugin 会自动给项目构建 debug
和 release
版本。两个版本的区别在于能否在安全设备(非 dev)上调试,以及 APK 如何签名。debug
使用通过通用的 name/password 对生成的密钥证书进行签名(为了防止在构建过程中出现认证请求)。release
在构建过程中不进行签名,需要自行签名。
这些配置是通过 BuildType
对象来完成的。默认情况下,debug
和 release
实例都会被创建。Android plugin 允许像创建其他 Build Type 一样自定义这两种类型。在 buildTypes 的 DSL 容器中进行配置:
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
}
jnidebug {
initWith(buildTypes.debug)// 复制
packageNameSuffix ".jnidebug"
jnidebugBuild true
}
}
}
另外,每个 Build Type 都会创建一个新的 assemble
例如:assembleDebug。
当 debug 和 release 构建类型被预创建的时候,assembleDebug 和 assembleRelease 会被自动创建。
buildType 的属性和方法
https://developer.android.com/reference/tools/gradle-api
属性 | 描述 |
---|---|
applicationIdSuffix | 应用 id 后缀 |
name | build type的名字 |
versionNameSuffix | 版本名称后缀 |
minifyEnabled | 是否混淆 |
proguardFiles | 混淆文件 |
signingConfig | 签名配置 |
multiDexEnabled | 是否拆成多个Dex |
方法 | 描述 |
---|---|
buildConfigField(type, name, value) |
添加一个变量生成 BuildConfig 类 |
consumerProguardFile(proguardFile) |
添加一个混淆文件进arr包 |
consumeProguardFile(proguardFiles) |
添加混淆文件进arr包 |
externalNativeBuild(action) |
配置本地的build选项 |
initWith(that) |
复制这个 build 类型的所有属性 |
proguardFile(proguardFile) |
添加一个新的混淆配置文件 |
proguradFiles(files) |
添加新的混淆文件 |
resValue(type, name, value) |
添加一个新的生成资源 |
setProguardFiles(proguardFileIterable) |
设置一个混淆配置文件 |
启用 proguard 混淆
可以为不同的 buildTypes 选择是否启用混淆,一般 release 发布版本是需要启用混淆的,这样别人反编译之后就很难分析你的代码,而我们自己开发调试的时候是不需要混淆的,所以 debug 不启用混淆。对 release 启用混淆的配置如下:
android {
buildTypes {
release {
minifyEnabled true
proguardFile 'proguard.cfg'
}
}
}
-
minifyEnabled
为 true 表示启用混淆 -
proguardFile
是混淆使用的配置文件
这里是 module 根目录下的 proguard.cfg 文件
启用 zipAlign
这个也是比较简单的,同样也是在 buildTypes 里配置,可以为不用的 buildTypes 选择时候开启zipAlign
android {
buildTypes {
release {
zipAlignEnabled true
}
}
}
配置应用的签名信息 android.signingConfigs{}
在 android.signingConfigs{}
下定义一个或者多个签名信息,在 buildTypes{}
配置使用即可。比如:
android {
signingConfigs {
release {
storeFile file("release.keystore")
keyAlias "tina"
keyPassword "123456"
storePassword "123456"
}
debug {
...
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.debug
}
}
}
-
storeFile
:签名证书文件 -
keyAlias
:别名 -
keyPassword
:key的密码 -
storePassword
:证书的密码
配好相关信息即可在 buildTypes 配置使用
多渠道打包 android.productFlavors{}
Android Gradle 给我们提供了productFlavors,让我们可以对生成的 APK 包进行定制,所以就有了多渠道。它支持与 defaultConfig 相同的属性,这是因为,defaultConfig
实际上属于ProductFlavor
android {
productFlavors {
dev{
}
google{
}
baidu{
}
}
}
这样当我们运行 assembleRelease 的时候就会生成 3 个 release 包,分别是 dev、google 以及 baidu。目前看这三个包除了文件名没有什么不一样,此时还没有定制,使用的都是 defaultConfig 配置。这里的 flavor 和 defaultConfig 是一样的,可以自定义其 applicationId、versionCode 以及 versionName等信息,比如区分不同包名:
android {
defaultConfig{
……
// Specifies a flavor dimension.
flavorDimensions "channel"
}
productFlavors {
dev{
applicationId "cn.tina.demo.dev"
}
google{
applicationId "cn.tina.demo.google"
}
}
}
flavorDimensions
(风格维度),所有变种都必须属于一个指定的变种维度,即一个产品变种组。必须将所有变种分配给某个变种维度;否则,您将收到如下所示的构建错误。如果给定的模块仅指定一个变种维度,则 Android Gradle 插件会自动将该模块的所有变种分配给该维度。
Error:All flavors must now belong to a named flavor dimension.
The flavor 'flavor_name' is not assigned to a flavor dimension.
注意:flavor 的命名不能与已存在的 Build Type 或者与 androidTest、test sourceSet 有冲突。
每一个 Build Type 都会生成新的 APK。Product Flavors 同样也会做这些事情:项目的输出将会组合所有的 Build Types 和 Product Flavors(如果有定义 Flavor)。
每一种组合(包含 Build Type 和 Product Flavor)就是一个 Build Variant(构建变种版本)。
例如,在之前的 Flavor 声明例子中与默认的 debug 和 release 两个 Build Types 将会生成 4 个 Build Variants:{dev, google}{debug, release}
- dev - debug
- dev - release
- google - debug
- google - release
项目中如果没有定义 flavor 同样也会有 Build Variants,只是使用的是 default 的 flavor/config,因为是无名称的,所以生成的 build variant 列表看起来就跟 Build Type 列表一样。
多定制的变种版本
扩展:flavorDimesions 可以设置多个,如
android {
defaultConfig{
……
// Specifies a flavor dimension.
flavorDimensions "channel","production"
}
productFlavors {
dev{
dimension "channel"
applicationId "cn.tina.demo.dev"
}
google{
dimension "channel"
applicationId "cn.tina.demo.google"
}
productA{
dimension "production"
}
productB{
dimension "production"
}
}
}
那么可以组合出4种(2*2)种产品,分别有 debug 和 release,一共是8个变种版本
构建和任务
我们前面提到每一个 Build Type 会创建自己的 assemble
,但是 Build Variants 是 Build Type 和 Product Flavor 的组合。
当使用 Product Flavor 的时候,将会创建更多的 assemble-type task。分别是:
assemble
允许直接构建一个 variant 版本,例如assembleDevDebug
。assemble
允许构建指定 Build Type 的所有 APK,例如assembleDebug
将会构建DevDebug
和GoogleDebug
两个 variant 版本。assemble
允许构建指定 flavor 的所有 APK,例如assembleDev
将会构建DevDebug
和DevRelease
两个 variant 版本。
另外 assemble task 会构建所有的 variant 版本。
android.defaultConfig
AndroidManifest 里的占位符
AndroidManifest.xml
这是一个很重要的文件,我们的很多配置都在这里定义。有时候我们的一些配置信息,比如一个第三方应用的 key,第三方统计分析的渠道号等也要在这里进行配置。
这里以友盟统计分析平台为例,演示这一功能的使用。在友盟统计分析中,我们需要根据渠道进行统计,比如 google,百度,应用宝等渠道的活跃新增等。
友盟的 SDK 是在 AndroidManifest
里配置一个 name 为 UMENG_CHANNEL
的 meta-data,这样这个 meta-data
的值就表示这个 apk 是哪个渠道,我们版本发布有几十个渠道,manifestPlaceholders
允许动态替换在 AndroidManifest 文件里定义的占位符。
如上 ${UMENG_CHANNEL_VALUE}
就是一个占位符,然后我们在 gradle 的 defaultConfig 里这样定义脚本:
android {
defaultConfig {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: 'dev']
}
}
上面代码块的意思就是我们的默认配置里 AndroidManifest 的 ${UMENG_CHANNEL_VALUE}
占位符会被 dev 这个字符串所替换,也就说默认运行的版本是一个开发版。以此类推,我们其他渠道的版本就可以这样定义:
android {
productFlavors {
google{
applicationId "org.demo.google"
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'google')
}
baidu{
applicationId "org.demo.baidu"
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'baidu')
}
}
}
这样有多少个渠道就做多少次这样的定义,即可完成分渠道统计。但是如果上百个渠道,这样一个个写的确太累,很麻烦,我们继续研究,同学们有没有发现,我们的渠道名字和我们的 flavorName 一样,我们用这个 flavorName
作为 UMENG_CHANNEL_VALUE
不就好了吗,可以批量的替换吗?当然可以,这又体现了我们 Gradle 的强大和灵活之处。
productFlavors.all { flavor ->
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",name)
}
循环每个 flavor,并把他们的 UMENG_CHANNEL_VALUE
设置为他们自己的 name 名字。
BuildConfig 自定义常量
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String BUILD_TYPE = "debug";
}
BuildConfig.java 是 Android Gradle 自动生成的一个 java 类文件,无法手动编译,但是可以通过 Gradle 控制,也就是说这里是动态可配置的,这里以生产环境和测试环境为例来说明该功能的使用。
我们在开发 App 的时候免不了要和服务器进行通信,我们的服务器一般都有生产和测试环境,当我们处理开发和测试的时候使用测试环境进行调试,正式发布的时候使用生成环境。以前的时候我们通过把不同的配置文件打包进 APK 中来控制,现在不一样了,我们有更简便的方法,这就是 buildConfigField
。
android {
defaultConfig {
buildConfigField'String','API_SERVER_URL','"http://test.cn/"'
}
productFlavors {
google{
buildConfigField 'String','API_SERVER_URL','"http://release.cn/"'
}
baidu{
buildConfigField 'String','API_SERVER_URL','"http://release.cn/"'
}
}
}
buildConfigField
一共有3个参数,
- 第一个是数据类型,是定义的常量值是一个什么类型,和 Java 的类型是对等的,这里是String。
- 第二个参数是常量名,这里是
API_SERVER_URL
。 - 第三个参数是常量值。如此定义之后,就会在 BuildConfig.java 中生成一个常量名为
API_SERVER_URL
的常量定义。默认配置的生成是:
public final static String API_SERVER_URL = "http://test.cn/"
当是 baidu 和 google 渠道的时候生成的就是 http://release.cn/ 了。这个常量可以在我们编码中引用。在我们进行打包的时候会根据 Gradle 配置动态替换。
我们发现一般渠道版本都是用来发布的,肯定用的是生产服务器,所以我们可以使用批处理来搞定这个事情,而不用在一个个渠道里写这些配置。
productFlavors.all { flavor ->
buildConfigField 'String','API_SERVER_URL','"http://release.org/"'
}
此外,比如 Gradle 的 resValue,也和 buildConfigField 一样,只不过控制生成的是资源,比如我们在 android 的 values.xml 定义生成的字符串。可以用它来动态生成我们想要的字符串,比如应用的名字,可能一些渠道会不一样,这样就可以很灵活的控制自动生成,关于 resValue 详细介绍请参考相关文档,这里不再举例说明。
参考:https://developer.android.com/studio/build