背景
上一阶段,我们了解了gradle的任务(Task)相关知识(如:定义/配置等)
知道了安卓在构建过程中,是通过一个个task串联起来的,如何实现的更多细节可以回顾【Gradle系列】Gradle之Task文章
接下来我们将学习gradle在安卓上的一些拓展,比如:setting/SourceSet/自定义插件等
setting
安卓开发过程中,我们经常会碰到 settings.gradle 文件,主要是用来包含模块,如:
include只是setting的其中一个功能而已,其还有很多功能(如:兼容maven等),只是安卓开发中用的比较少
settings.gradle文件对应的解析类是 org.gradle.api.initialization.Settings
Settings类的执行是在Gradle生命周期的初始化阶段,是初始化阶段最重要的类
翻看源码可以看到,比较常用的include对应的代码为:
void include(String... var1);
void includeFlat(String... var1);
其他部分的代码:
Settings getSettings();
@Incubating
ScriptHandler getBuildscript();
File getSettingsDir();
File getRootDir();
ProjectDescriptor getRootProject();
ProjectDescriptor project(String var1) throws UnknownProjectException;
@Nullable
ProjectDescriptor findProject(String var1);
ProjectDescriptor project(File var1) throws UnknownProjectException;
@Nullable
ProjectDescriptor findProject(File var1);
StartParameter getStartParameter();
Gradle getGradle();
void includeBuild(Object var1);
void includeBuild(Object var1, Action<ConfigurableIncludedBuild> var2);
BuildCacheConfiguration getBuildCache();
void buildCache(Action<? super BuildCacheConfiguration> var1);
void pluginManagement(Action<? super PluginManagementSpec> var1);
PluginManagementSpec getPluginManagement();
@Incubating
void sourceControl(Action<? super SourceControl> var1);
@Incubating
SourceControl getSourceControl();
@Incubating
void enableFeaturePreview(String var1);
SourceSet
SourceSet与项目结构有关
安卓默认情况下,代码是放在src/main/java,资源放在src/main/res中,当然还有其他(如:so等)
我们可以通过改变SourceSet属性,自己指定代码/资源放的目录(这个比maven强,因为maven是不可以指定或者添加的)
(1)例子1: 改变so存放位置
默认情况,so存放在app/src/main/jniLibs中,现在改为app/libs下,具体实现如下:
android {
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
结果:
这样我们就可以把so放到libs下,编译等时候就会去这里加载
(2)例子2: 资源模块管理
我们日常开发中,资源(如:图片/布局等)一般都是放在src/main/res下面
但是,在大型app中(如:虎牙/CC直播等)涉及到的模块是错综复杂的,如果全部都放在同一个位置,那么维护起来就很费劲
所以,我们可以添加资源的目录,具体实现如下:
android {
sourceSets {
main {
res.srcDirs = ['src/main/res', 'src/main/res-davi']
}
}
}
结果:
可以看到,资源目录由一个变为了两个,这样的话不同模块放不同的资源目录,进而实现 资源模块的管理
自定义插件
前面我们学习了task的使用,可以看到其是写在gradle文件里面的;
随着功能的越来越复杂,那么这种模式下那么gradle文件就会越来越多,这样相比我们写java代码中分模块来比可读性和维护性就没那么好
所以自定义Gradle插件可以比较好的组织这些task,以功能为单元去组织系列task,进而实现,比如我们经常使用的gradle插件:
apply plugin: 'groovy'
apply plugin: 'com.android.application'
下面我们就来看下自定义插件的流程:
新建模块
在AS中新建一个模块,然后把代码/资源删除,gradle文件清空,把目录调整为如下:
配置编译需要的依赖
在清空的gradle文件中添加如下内容:
//groovy 插件
apply plugin: 'groovy'
//maven仓库上传需要用到的
apply plugin: 'maven'
//引入gradle的API代码
dependencies {
implementation gradleApi()
}
sourceSets {
main {
//指定groovy源码的位置
groovy {
srcDir 'src/main/groovy'
}
//指定资源的位置
resources {
srcDir 'src/main/resources'
}
}
}
建立插件入口和相关配置
(3.1)建立代码入口:
package com.davi.pkg
import org.gradle.api.Action
import org.gradle.api.Plugin
import org.gradle.api.Project
public class DaviPluDemo implements Plugin<Project> {
@Override
void apply(Project project) {
println '【DaviPluDemo】-- apply start-- ' + project.name
println '【DaviPluDemo】-- apply end-- ' + project.name
}
}
(3.2) 配置插件入口文件:
首先是新建配置文件
红色框的名字比较重要,这个代表的就是在使用的时候的名字:
apply plugin: 'com.demo.plugin'
在使用者找到配置入口后,然后就是配置入口关联代码入口(com.davi.pkg.properties文件里面的内容):
implementation-class=com.davi.pkg.DaviPluDemo
编译插件并且上传maven
编译就不用讲啦,直接模块里面点build
然后就是上传maven,上传之前需要在gradle中配置下上传相关:
uploadArchives {
repositories {
mavenDeployer {
//设置插件的GAV参数
pom.groupId = 'com.davi.pkg'//你的包名
pom.artifactId = 'dp'//这个随意起一个,发布后就是这个插件的名称
pom.version = '1.1.7'//版本号
//文件发布到下面目录8
repository(url: uri('../repo'))
}
}
}
然后任务中点击uploadArchives来上传,具体按钮如下:
使用插件
这里我们是在app模块里面使用插件,所以在app模块的gradle文件中添加如下配置:
buildscript {
repositories {
maven {
//本地Maven仓库地址
//这里是发布在本地文件夹了
url uri('../repo')
}
}
dependencies {
//格式为 groupId : artifactId : version
classpath 'com.davi.pkg:dp:1.1.7'
}
}
apply plugin: 'com.davi.pkg'
效果
app模块引入成功之后,编译app模块,这个可以看到插件的运行效果:
Gradle自定义插件里添加task
定义
在插件模块里面代码位置,擦创建task类,代码如下:
package com.davi.pkg.task
import com.davi.pkg.bean.ConfigBean
import com.davi.pkg.DaviPluDemo
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class DaviTaskPlu extends DefaultTask {
DaviTaskPlu() {
group = 'davi'
description = '自定义插件中的任务:DaviTaskPlu'
}
/**
* TaskAction
* - 被这个注解过的,那么方法就会在gradle的生命周期中的执行阶段被执行
* */
@TaskAction
void onAction() {
println '【DaviTaskPlu】【onAction】---> start'
println '【DaviTaskPlu】【onAction】---> end'
}
}
插件中调用定义的task
public class DaviPluDemo implements Plugin<Project> {
public static String NAME_PLU_CONFIG = 'pluConfig'
@Override
void apply(Project project) {
println '【DaviPluDemo】-- apply start-- ' + project.name
onTask(project)
println '【DaviPluDemo】-- apply end-- ' + project.name
}
void onTask(Project project) {
/**
* 自定义task的执行
* 创建了一个名为 name-davi 的任务,然后映射到我们自定义的任务的类 DaviTaskPlu。
* */
project.getTasks().create("name-davi", DaviTaskPlu.class, new Action<DaviTaskPlu>() {
@Override
void execute(DaviTaskPlu t) {
t.onAction()
}
})
}
}
使用效果
插件改动后,同样编译插件,改版本好,然后上传maven; 最后是app模块那边使用地方提高版本号,编译使用,具体效果如下:
安卓其他Gradle拓展简要
我们日常开发安卓的时候,经常用到下面的系列配置:
android {
compileSdkVersion this.compileSdkVersion
buildToolsVersion this.buildToolsVersion
defaultConfig {
applicationId this.applicationId
minSdkVersion this.rootProject.ext.android.minSdkVersion
targetSdkVersion mTargetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
这些都是安卓在gradle上的拓展,如上面的defaultConfig,,buildTypes 除了常用的这些,还有很多其他的配置,具体可以查看gradle的源码:
com.android.build.gradle.AppExtension
com.android.build.gradle.TestedExtension
com.android.build.gradle.BaseExtension
怎么看?比如buildTypes的源码位置:
其他(如:变体)
(1)例子1: 实现改apk输出的名字思路
代码:
this.afterEvaluate {
//有图.....
this.android.applicationVariants.each { variant ->
//debug || release
println '【applicationVariants】name is : ' + variant.name
//debug || release
println '【applicationVariants】baseName is : ' + variant.baseName
//1.0
println '【applicationVariants】versionName is : ' + variant.versionName
/***
* 【1】实现改apk输出的名字
* */
//output.outputFile = apkFile //重命名后的apk文件指向output,实现apk重命名
variant.outputs.each { output ->
//ktplay_android_sdk_CN_v4/DaviPlu/app/build/outputs/apk/debug/app-debug.apk
//ktplay_android_sdk_CN_v4/DaviPlu/app/build/outputs/apk/release/app-release-unsigned.apk
File outputFile = output.outputFile
println '【applicationVariants】【output.outputFile】 path : ' + outputFile.path
println '【applicationVariants】output name : ' + output.name
}
}
}
运行效果:
根据运行的效果可以看出,output.outputFile指向的就是我们生成的apk(包含debug和release),如果要重新命名apk目录(比如:根据渠道名字命名),只需要重定向下outputFile即可,如:
output.outputFile = apkFile //重命名后的apk文件指向output,实现apk重命名
这里基于本章重点不在此,所以后续项目中再落实具体的项目实现,这里先留个变体的概念应用先;
另外,有一个比较重要的点需要留意,在访问变体applicationVariants的时候,会触发创建所有的任务;所以我们重定向了outputFile,也就会触发重新构建,进而实现改名字这样
(2)例子2: 变体中,找到某个任务,然后插入自己要执行的任务 代码:
this.afterEvaluate {
//有图....
this.android.applicationVariants.each { variant ->
//debug || release
println '【applicationVariants】buildType.name is : ' + variant.buildType.name
/**
* 【2】变体中,找到某个任务,然后插入自己要执行的任务(利用doLast/doFirst等)
* */
Task checkManifest = variant.checkManifest
checkManifest.doFirst {
println '【applicationVariants】在 checkManifest 任务执行前插入的代码块 '
}
}
}
运行效果:
小结:变体是android在gradle的拓展之一,日常项目应用中是很广泛的,这里先简单介绍下,后续项目使用的时候综合讲
源码地址
基于有些朋友咨询到源码相关,故把代码上传到了github,需要的朋友可以下载来看看
具体戳这里>>>
PS:代码目前还没有体系整理,后面会在项目中的时候再统一整理
结尾
哈哈,该篇就写到这里(一起体系化学习,一起成长)
Tips
更多精彩内容,请关注 “Android热修技术” 微信公众号