我们需要知道所以Android插件,其实就是利用Gradle构建的插件。
[Android Gradle 插件分类]
(1)App插件: com.android.application
(2)Library插件:com.android.library
(3)Test插件:com.android.test
[Android插件何依赖在哪里引用]
(1)Android插件引用如何引用
在Android项目根目录下的build.gradle
下找到如下代码:
buildscript {
repositories { // 下载插件用到的仓库
google() // 仓库1
jcenter() // 仓库2
customName() // 仓库3
}
dependencies { // 引用各种插件
// android Gradle 插件引用
classpath 'com.android.tools.build:gradle:3.6.3'
// 其它插件
classpath 'xxx.xxx.xxx:1.0.1'
}
}
具体说明请看注释。
(2)各Project下的依赖引用方式(这里的Project其实就是Module)
每个Project中都存在独立的build.gradle
文件,随便选择一个Project,贴下如下代码:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
dependencies
函数下定义了一系列的依赖,这些依赖也是从仓库中下载的,那么,在Andoid工程中,除了要引用Android插件的远程仓库之外,还需要引用各Project引用依赖的远程仓库。
在Android工程根目录下的build.gradle
文件中,还存在这样的代码:
allprojects {
repositories {
google()
jcenter()
}
}
allprojects
函数下引用了所有Project依赖的仓库。
[插件如何应用到某个Project中]
在某个Project中的build.gradle
文件下应用插件,代码如下:
apply plugin: 'pluginID'
[包名]
通常情况下,应用的包名由applicationId
指定,它在主Project的build.gradle
文件下的
defaultConfig
函数中被定义,applicationId
是ProjectFlavor的一个属性,如图:
如果不设置applicationId
,那么应用的包名由AndroidManifest.xml文件中manifest
标签下的package
字段来指定,如图:
[签名]
使用signingConfigs
方法,完成签名配置,本文不做详细介绍。
[构建的应用类型]
应用的类型由buildTypes
函数定义,默认情况下,有两种类型,分别是debug
和release
,也就是说,即使没有buildTypes
函数,Project也默认存在两种类型,当然,如果需要,还可以新增任意类型,如图新增了custom
类型:
buildTypes
常常和productFlavors
一起使用,比如:
那么,在Build Variants中的显示就会变化,如图:
productFlavors
是Gradle非常重要的知识,当项目存在多个特性时,以前的做法是拉取其它分支,在某各固定的仓库分支中实现对应的特性,productFlavors
的出现可以避免频繁的切换分支。
productFlavors
将在下一章单独讲解。
在某类型中,可以使用如下属性:
- applicationIdSuffix:applicationId的后缀;
- debuggable:是否生成一个可供调试的apk;
- jniDebuggable:是否生成一个可以调试jni的apk;
- minifyEnabled:是否启动Proguard混淆;
- multiDexEnabled:是否启动自动拆分多个Dex的功能;
- proguardFile:Proguard混淆的配置文件(只能配置一个);
- proguardFiles:Proguard混淆的配置文件(可以同时配置多个);
- shrinkResources:是否自动清理未使用的资源,默认为false;
- signingConfig:签名配置;
[使用混淆]
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
看如上代码,默认情况下,minifyEnabled
的值是false,即不启用混淆,想要使用混淆,这个参数必须设置为true;
其次,就是混淆文件了,可以使用proguardFile
指定某个混淆文件,但只能指定一个文件;
使用proguardFiles
可以指定多个文件,如上代码指定了两个文件,分别是“getDefaultProguardFile('proguard-android-optimize.txt')”和“proguard-rules.pro”,后者在项目中就可以找到,前者在Android SDK目录,具体路径是:\tools\proguard\proguard-android-optimize.txt
,这个目录下还有一个混淆文件proguard-android.txt
,proguard-android.txt
是优化前的,proguard-android-optimize.txt
是优化后的。
[zipalign优化]
zipalign是Android提供的一个整理优化apk的工具,它能提高系统和应用的运行效率,更快地读写apk中的资源,降低内存的使用,所以对于要帆布的App,在发布之前一定要使用zipalign进行优化,具体使用如下:
在Android工程中,只需要将zipAlignEnabled
属性设置为true即可。
[如何修改自动生成apk的名称]
如图所示,Android工程默认打包的名称是app-debug.apk
,这样的命名方式只能区分安装包是debug
还是release
,如果项目加入了多渠道打包的功能,举例如下:
//多渠道
productFlavors {
xiaomi{
}
vivo{
}
oppo{
}
}
显然,在多渠道打包的项目中,我们需要重新定制一个安装包的命名方式,这样才能区分安装包属于哪个渠道。
代码如下:
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "${variant.name}_v${variant.versionName}.apk"
}
}
最终生成apk的路径、名称见下图:
[版本号的管理]
通常情况下,Android项目的版本号是在主模块下的build.gradle
中定义的,代码如下:
defaultConfig {
applicationId "com.example.m"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
versionCode
:版本号
versionName
:版本名称
对于用户而言,只有versionName
可见,所以对用户而言,versionName
就是版本号。
随着项目业务的迭代,模块越来越多,导致很难找到主Module;在组件化架构的项目中,任何Module都可能是一个主Module,每个组件都会定义版本号,如果需要修改版本号,需要修改所有组件中的版本,并且版本号保持一致,这种情况下,将版本号统一管理是必要的。
[方法一]
将版本号定义在根目录下的build.gradle
文件中,添加一些列的版本信息
ext {
minSdkVersion = 19
targetSdkVersion = 29
compileSdkVersion = 29
buildToolsVersion = "29.0.3"
versionCode = 1
versionName = "1.0"
}
这些属性已经成为了公共信息,可以直接被调用。
在对应的Module中可以引用这些参数,代码如下:
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.example.m"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
[方法二]
将配置信息单独写在一个文件中
在项目的根目录下,创建文件config.gradle
,文件中的内容如下:
ext {
appMinSdkVersion = 19
appTargetSdkVersion = 29
appCompileSdkVersion = 29
appBuildToolsVersion = "29.0.3"
appVersionCode = 1
appVersionName = "1.0"
}
在Module下引用这个文件
apply from: '../config.gradle'
在Module下使用:
compileSdkVersion appCompileSdkVersion
buildToolsVersion appBuildToolsVersion
defaultConfig {
applicationId "com.example.m"
minSdkVersion appMinSdkVersion
targetSdkVersion appTargetSdkVersion
versionCode appVersionCode
versionName appVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
[如何隐藏签名信息]
有时候,为了开发方便,我们会将App的打包签名会放在个人计算机本地,如果多人开发可能还会将签名放到项目中,其实这样是不对的。
签名的信息是非常重要的,需要进行保密,所以采用的方案是:将release的签名放在服务器,将debug的签名放在项目中,并且服务器的签名信息必须保密,代码如下:
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
signingConfigs {
def appStoreFile = System.getenv("STORE_FILE")
def appStorePassword = System.getenv("STORE_PASSWORD")
def appKeyAlias = System.getenv("KEY_ALIAS")
def appKeyPassword = System.getenv("KEY_PASSWORD")
//当不能从环境变量里获取签名信息的时候,就使用项目中自带的debug签名
if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){
appStoreFile = "debug.keystore"
appStorePassword = "android"
appKeyAlias = "androiddebugkey"
appKeyPassword = "android"
}
release {
storeFile file(appStoreFile)
storePassword appStorePassword
keyAlias appKeyAlias
keyPassword appKeyPassword
}
}
}
[动态配置AndroidManifest文件]
在AndroidManifest中定义变量AMAP_KEY
然后在build.gradle
中添加如下代码:
buildTypes {
debug {
manifestPlaceholders.put("AMAP_KEY", "111111111111111111")
}
release {
manifestPlaceholders.put("AMAP_KEY", "2222222222222222222")
}
}
Android Gradle提供了manifestPlaceholders
占位符,在打包的过程中,可以替换AndroidManifest中的变量。
[自定义BuildConfig参数]
项目打包之后,在build文件夹中会自动生成BuildConfig
配置文件,如下:
这些属性都是些项目中的获取的,所以我们可以使用那些属性,如下图:
但是,如果可以动态的在BuildConfig文件中添加属性就更加灵活了,使用Android Gradle的buildConfigField
字段可以动态的在BuildConfig中添加属性,代码如下:
//多渠道
productFlavors {
xiaomi{
buildConfigField 'String', 'URL', '"https://www.xiaomi.com"'
buildConfigField 'String', 'USERNAME', '"xiaomi"'
}
vivo{
buildConfigField 'String', 'URL', '"https://www.vivo.com"'
buildConfigField 'String', 'USERNAME', '"vivo"'
}
oppo{
buildConfigField 'String', 'URL', '"https://www.oppo.com"'
buildConfigField 'String', 'USERNAME', '"oppo"'
}
}
根据不同的Flavor,BuildConfig属性值也会不同,最新生成的BuildConfig属性如下(就拿xiaomi举例):
[自定义resValue]
在Android工程的res/values
文件夹下定义了各种资源,然而Android Gradle可以做到动态自定资源,看下代码:
productFlavors {
xiaomi{
resValue 'string', 'who', 'xiaomi'
}
vivo{
resValue 'string', 'who', 'vivo'
}
oppo{
resValue 'string', 'who', 'oppo'
}
}
编译之后,我们可以在/build/generated/res/resValues/xiaomi/debug/values/gradleResValues.xml
文件下找到对应的资源
Android Gradle的resValue
方法,后面传递三个参数,分别是:Type、Name、Value,正好与资源定义格式对应,根据类似的做法,resValue
还可以定义其它类型的资源。
[dex选项配置]
dexOptions {
incremental true // 是否启动dx的增量模式,默认为false。增量模式速度更快,但目前有很多限制,慎用
javaMaxHeapSize '4g' // 执行dx命令时,分配的最大堆内存,主要是为了解决执行dx内存不够的问题
jumboMode true // 是否开启jumbo模式,如果函数超过65535个,那么就需要强制开启jumbo模式才可以构建成功
preDexLibraries true // 是否预执行dex Libraries库工程,开启后会大大提高增量构建的速度,但是会影响clean构建的速度,默认开启。如果使用dx的--multi-dex选项生成多个dex,需要设置为false
threadCount 2 // 执行dx使用的线程数量
}
[函数突破65535个的解决方案]
当Android工程中的函数总数超过65535个时,打包构建会失败,这时需要dex分包处理,解决方案如下:
【第一步】
开启MultiDex
【第二步】
Android 5.0只有一个Dex,为了兼容5.0,还需要做下一步处理
集成MultiDexApplication
import androidx.multidex.MultiDexApplication;
public class MyApplication extends MultiDexApplication {
}
或者添加MultiDex.install(this);
import androidx.multidex.MultiDex;
public class MyApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
[本章完...]