Gradle是Android项目开发环境的一部分,Android Studio每次配置编译时都需要使用Gradle。
一.Gradle基础:
Gradle本质上时一个自动构建工具,使用基于Groovy的特定领域语言(DSL)来声明项目设置。使用Groovy最大哦的原因就是Groovy基本语法和Java一样,最大程度适应Java开发。当利用Groovy编写自定义插件时,语法并没有太大差异,仅仅是配置机制需要调整。
Android Studio构建工程时,就是利用Gradle编写的插件来加载工程配置和编译文件。
- 工程根目录的build.gradle时配置整个工程引用的Gradle文件,能够配置获取Gradle引用仓库的地址。
-
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { //构建脚本引用 repositories { //插件仓库配置 google() jcenter() //jcenter本质上是一个maven仓库 } dependencies { //依赖插件 classpath 'com.android.tools.build:gradle:3.4.1' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { //全部项目的配置 repositories { //全部项目引用的基础仓库配置 google() jcenter() } } task clean(type: Delete) { //声明任务 delete rootProject.buildDir //删除主路径buildDir文件 } dependencies { //第三方库的依赖 compile 'com.alibaba:arouter-api:1.1.0' annotationProcessor 'com.alibaba:arouter-compiler:1.1.1' compile('com.facebook.fresco:fresco:0.10.0') { exclude group:'' } }
根目录的build.gradle文件会影响工程中其他模块的build.gradle文件的引用仓库的路径
- 当创建出一个新的module时,每个module都会有一个build.gradle文件。
-
apply plugin: 'com.android.application' //引用编译构建Gradle插件 android { compileSdkVersion 28 //编译的工具对应版本 buildToolsVersion "29.0.0" //编译工具对应版本 defaultConfig { //默认配置 applicationId "com.example.demo1" minSdkVersion 15 //最低支持版本 targetSdkVersion 28 //支持的目标版本 versionCode 1 //版本号 versionName "1.0" //版本名 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" //测试脚本 } buildTypes { //构建类型 release { //release版本配置 minifyEnabled false //是否打开混淆 shrinkResources true //是否打开资源混淆 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' //混淆文件 } } productFlavors { //用户版本 client { manifestPlacehoders = [ channel:"10086", //渠道号 verNum:"1", //版本号 app_name:"Gank" //app名 ] } //服务版本 server { manifestPlacehoders = [ channel:"10087", //渠道号 verNum:"1", //版本号 app_name:"Gank服务版" //app名 ] } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' classpath 'com.android.tools.build:gradle:3.1.1' classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' implementation 'com.android.support:localbroadcastmanager:28.0.0'// add plugin // implementation "io.reactivex.rxjava3:rxjava:3.x.y" }
build.gradle的第一行代码重点引用了构建需要用到的Gradle插件工具库。对比编写Java代码,可以认为相当于"import"了一个Java工具库。
每个build.gradle自身是一个Project对象,project.apply()会加载某个工具库到project对象中;
apply plugin:"XXX"的方法会将project对象传递入工具库,然后再通过插件中的Groovy文件来操作project对象的属性,以完善配置初始化信息;
其中android{}和dependencies{}是函数方程式,使用闭包函数的编写方式。其真实调用android{}和dependencies{}方法。方法中会设置project的属性;
每个Project中包含很多Task的构建任务,每个Task中可以包含很多Action动作,每个Action相当于一个代码块,包含很多需要被执行的代码。
- Gradle对Android工程配置的地方,是主目录中的settings.gradle的文件;
- 当添加一个module时,Gradle会自动添加一个文件路径到settings.gradle中的include函数中,声明这些文件夹以一个模块的形式存在,将此模块引用到Gradle中进行编译构建。如移除其中一项,对应的文件夹将不会被Gradle插件识别,Android Studio也不会认为此文件夹为一个module目录;
- 每个Gradle文件都是一个project对象,Gradle管理这个project的生命周期
Gradle的生命周期settings.gradle(初始化)->build.gradle(配置)->gradle(构建):
1.初始化阶段会读取根目录中的settings.gradle的include信息,决定哪些工程会加入构建过程,并且创建project实例;
2.配置阶段会按引用树去执行所有工程的build.gradle脚本,配置project对象,一个对象由多个任务组件。此阶段也会去创建/配置Task及相关信息;
3.运行阶段会根据Gradle命令传递过来的Task名称,执行相关依赖。
二.版本参数优化:
每个module的build.gradle文件都拥有一些必要的属性,同一个Android工程中,在不同模块中要求这些属性一致,如complieSDKVersion/buildToolVersion等。如引用不一致,属性不会被合并引入工程中,这样一方面会造成资源的重复/包量增大,另一方面会降低编译效率。
那就必有一个统一的/基础的Gradle配置,以下有三种方案可选:
第一种方式使用共同参数的方式进行配置:
- 创建一个common_config.gradle文件;
- 在common_config.gradle中编写一些简单的变量信息;
-
project.ext相当于一个变量类,其中属性可以看作类中的静态变量。project.ext { complieSdkVersion = 28 buildToolVersion = "29.0.0" minSdkVersion = 15 targetSdkVersion = 28 applicationId "com.example.demo1" }
- 在module的首行build.gradle中添加common_config文件,并通过引用类似引用静态变量的方式来引用属性。
-
apply plugin: 'com.android.library' apply plugin: '${rootProject.rootDir}/common_config.gradle' //引用额外的配置 android { compileSdkVersion project.ext.complieSdkVersion buildToolsVersion project.ext.buildToolVersion defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }
第二种方式时使用Android对象配置:
因为apply from引用了common_build.gradle,所以可以引用build.gradle中的Android对象。android{}中提供了android这个变量,可以进一步简化android{}的参数编写来简化代码。在common_build.gradle的project.ext中添加一个闭包方法来指定project对象变量。
-
setDefaultConfig = { extension -> //闭包参数extension相当于android对象 extension.compileSdkVersion compileSdkVersion extension.buildToolsVersion buildToolsVersion extension.defaultConfig { minSdkVersion minSdkVersion targetSdkVersion targetSdkVersion testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } extension.dataBinging{ enabled = true } }
然后在build.gradle中就可以使用setDefaultConfig了,类似属性设置一样,实质上是函数调用。
-
android { project.ext.setDefaultConfig android //调用配置函数 .... }
第三种方式时使用project对象配置:
-
//设置app 设置 setAppDefaultConfig = { extension -> extension.apply plugin:'com.android.application' //引入application插件库 extension.descriptaion "app" setAndroidConfig extension.android setDependencies extension.dependencies } //设置Lib配置 setLibDefaultConfig = { extension -> extension.apply plugin:'com.android.application' extension.descriptaion "lib" setAndroidConfig extension.android setDependencies extension.dependencies } //设置Android配置 setAndroidDefaultConfig = { extension -> extension.compileSdkVersion 28 extension.buildToolsVersion "29.0.0" extension.defaultConfig { minSdkVersion 15 targetSdkVersion 28 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { arguments = [moduleName:extension.project.getName()] } } } extension.dataBinging{ enabled = true } } //设置依赖 setDependencies = { extension -> extension.compile fileTree(dir: 'lib','include',['*.jar']) //每个module都需要引用路由apt插件库才能生成相应的代码,这里无须重复编写每个module extension.annotationProcessor 'com.alibaba:arouter-compiler:1.1.1' }
在build.gradle中只需要传入project对象到闭包函数中即可:
-
//application module配置 apply plugin: '${rootProject.rootDir}/common_config.gradle' //引用额外的配置 project.ext.setAppDefaultConfig project //library module配置 apply plugin: '${rootProject.rootDir}/common_config.gradle' //引用额外的配置 project.ext.setLibDefaultConfig project
因为组件化会用到application module和library module,所以需要分开编写共性的方法,可以为一些公用的库添加引用。
三.调试优化:
业务模块调试,将单一模块做成app启动,然后用于调试测试。这样保证单独模块可以分离调试。
需要变更的地方:
- 业务模块是Library module,独立调试需要将模块做成application module才能引入app构建流程。在common_config.gradle中,只需替换对象即可。
- 每个Application都需要配置ApplicationId
可以使用直接配置属性:
applicationId project.ext.applicationId
也可以在common_config中添加函数配置:
-
setAppDefaultConfig = { extension -> ... extension.android.defaultConfig{ applicationId applicationId+"."+extension.getName() } ... }
这里extension.getName(),在默认ApplicationId后添加module名字,用于区分不同module产生的单独的app。
- 在src中建立debug文件夹,同main文件夹目录类似,用于放置需要调试AndroidManifest.xml文件/Java文件/res资源文件。AndroidManifest文件需要设置默认启动activity文件,不给启动activity设置Default属性,否则会导致安装后activity被启动的问题:
-
<activity android:name=".DebugActivity" android:theme="@style/AppTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>
- 在common_config中需要声明单独模块调试变量,如:
-
project.ext{ ... isNewDebug = false ... }
在模块build.gradle中变量isNewDebug作为开关:
-
if(project.ext.isNewDebug){ project.setLibDefaultConfig project //设置lib配置 }else { project.setAppDefaultConfig project //设置app配置 }
在sourceSets资源配置,配置AndroidManifest的地址及res资源地址,编译构建是Gradle会选取内的资源:
-
sourceSets { main{ if (project.ext.isNewDebug) { manifest.srcFile 'src/debug/AndroidManifest.xml' res.srcDirs = ['src/debug/res','src/main/res'] }else { manifest.srcFile 'src/main/AndroidManifest.xml' resources { //排除Java/debug文件夹下的所有文件 exclude 'src/debug/*' } } } }
- app module需要移除已经单独调试的模块的依赖:
-
dependencies { if(!project.ext.isNewDebug){ compile project(':news') } }
四.资源引用配置:
Gradle有多种引用的方式
- 使用sourceSets的方式指定文件的路径。sourceSets还可以指定更多的资源设置属性的路径:
-
sourceSets { main{ manifest.srcFile 'src/debug/AndroidManifest.xml' //指定AndroidManifest res.srcDirs = ['src/main/res'] //指定Java文件路径 resources.srcDirs = ['src'] //全部资源文件路径 aidl.srcDirs = ["src"] //指定aidl文件路径 renderscript.srcDirs = ['src'] //指定renderscript文件路径 res.srcDirs = ['src'] //res资源文件路径 assets.srcDirs = ['assets'] //指定资产文件路径 } }
- 可以动态添加res资源,在buildtype和productFlavor中定义resValue:
-
resValue "string","app_name",....
- 可以指定特定尺寸的资源,也可以在buildType和productFlavor中定义:
-
resConfigs "hdpi","xhdpi","xxhdpi"
- Gradle加载优先级为BuildType->productFlavor->main->dependencies。