写点什么

Android Gradle 学习笔记整理,阿里 Android 面试必问

用户头像
Android架构
关注
发布于: 刚刚

v -> println v}static def testMethod(Closure closure){closure('闭包 test')}testMethod v


其中定义的 v 就为闭包,testMethod 为一个方法,传入参数为闭包,然后调用闭包.

解释 apply plugin: 'xxxx'和 dependencies{}

准备工作,看 gradle 的源码

我们先把子项目的 build.gradle 改为如下形式


apply plugin: 'java-library'repositories {mavenLocal()}dependencies {compile gradleApi()}


这样,我们就可以直接看 gradle 的源码了,在 External Libraries 里如下


解释

进入 build.gradle 点击 apply 会进入到 gradle 的源码,可以看到


//PluginAware/**


  • Applies a plugin or script, using the given options provided as a map. Does nothing if the plugin has already been applied.

  • <p>

  • The given map is applied as a series of method calls to a newly created {@link ObjectConfigurationAction}.

  • That is, each key in the map is expected to be the name of a method {@link ObjectConfigurationAction} and the value to be compatible arguments to that method.

  • <p>The following options are available:</p>

  • <ul><li>{@code from}: A script to apply. Accepts any path supported by {@link org.gradle.api.Project#uri(Object)}.</li>

  • <li>{@code plugin}: The id or implementation class of the plugin to apply.</li>

  • <li>{@code to}: The target delegate object or objects. The default is this plugin aware object. Use this to configure objects other than this object.</li></ul>

  • @param options the options to use to configure and {@link ObjectConfigurationAction} before “executing” it*/void apply(Map<String, ?> options);


用 Groovy 语法很清楚的解释,apply 其实就是一个方法,后面传递的就是一个 map,其中 plugin 为 key.


那么 dependencies{}也是一样


//Project/**


  • <p>Configures the dependencies for this project.

  • <p>This method executes the given closure against the {@link DependencyHandler} for this project. The {@link

  • DependencyHandler} is passed to the closure as the closure's delegate.

  • <h3>Examples:</h3>

  • See docs for {@link DependencyHandler}

  • @param configureClosure the closure to use to configure the dependencies.*/void dependencies(Closure configureClosure);


dependencies 是一个方法 后面传递的是一个闭包的参数.


问题:思考那么 android {}也是一样的实现吗? 后面讲解

Gradle Project/Task

在前面章节中提到 gralde 初始化配置,是先解析并执行 setting.gradle,然后在解析执行 build.gradle,那么其实这些 build.gradle 就是 Project,外层的 build.gradle 是根 Project,内层的为子 project,根 project 只能有一个,子 project 可以有多个.


我们知道了最基础的 gradle 配置,那么怎么来使用 Gradle 里面的一些东西来为我们服务呢?

Plugin

前面提到 apply plugin:'xxxx',这些 plugin 都是按照 gradle 规范来实现的,有 java 的有 Android 的,那么我们来实现一个自己的 plugin.


把 build.gradle 改为如下代码


//app build.gradleclass LibPlugin implements Plugin<Project>{@Overridevoid apply(Project target) {println 'this is lib plugin'}}apply plugin:LibPlugin


运行./gradlew 结果如下


Configure project :appthis is lib plugin

Plugin 之 Extension

我们在自定义的 Plugin 中要获取 Project 的配置,可以通过 Project 去获取一些基本配置信息,那我们要自定义的一些属性怎么去配置获取呢,这时就需要创建 Extension 了,把上述代码改为如下形式。


//app build.gradleclass LibExtension{String versionString message}class LibPlugin implements Plugin<Project>{@Overridevoid apply(Project target) {println 'this is lib plugin'//创建 Extensiontarget.extensions.create('libConfig',LibExtension)//创建一个 tasktarget.tasks.create('libTask',{doLast{LibExtension config = project.libConfigprintln config.versionprintln config.message}})}}apply plugin:LibPlugin//配置 libConfig {version = '1.0'message = 'lib message'}


配置完成后,执行./gradlew libTask 得到如下结果


Configure project :appthis is lib pluginTask :lib:libTask1.0lib message


看完上述代码,我们就知道 android {} 其实他就是一个 Extension, 他是由 plugin 'com.android.application'或者'com.android.library' 创建。

Task

上述代码中,创建了一个名字为 libTask 的 task,gradle 中创建 task 的方式由很多中, 具体的创建接口在 TaskContainer 类中


//TaskContainerTask create(Map<String, ?> options) throws InvalidUserDataException;Task create(Map<String, ?> options, Closure configureClosure) throws InvalidUserDataException;Task create(String name, Closure configureClosure) throws InvalidUserDataException;Task create(S


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


tring name) throws InvalidUserDataException;<T extends Task> T create(String name, Class<T> type) throws InvalidUserDataException;<T extends Task> T create(String name, Class<T> type, Action<? super T> configuration) throws InvalidUserDataException;


Project 不可以执行跑起来,那么我们就要定义一些 task 来完成我们的编译,运行,打包等。com.android.application 插件 为我们定义了打包 task 如 assemble,我们刚才定义的插件为我们添加了一个 libTask 用于输出。


Task API

我们看到创建的 task 里面可以直接调用 doLast API,那是因为 Task 类中有 doLast API,可以查看对应的代码看到对应的 API


Gradle 的一些 Task

gradle 为我们定义了一些常见的 task,如 clean,copy 等,这些 task 可以直接使用 name 创建,如下:


task clean(type: Delete) {delete rootProject.buildDir}

依赖 task

我们知道 Android 打包时,会使用 assemble 相关的 task,但是仅仅他是不能直接打包的,他会依赖其他的一些 task. 那么怎么创建一个依赖的 Task 呢?代码如下


task A{println "A task"}task B({println 'B task'},dependsOn: A)


执行./graldew B 输出


A taskB task

自定义一个重命名 APP 名字的插件

通过上述的一些入门讲解,大概知道了 gradle 是怎么构建的,那现在来自定义一个安卓打包过程中,重命名 APP 名字的一个插件。


上述在 build.gradle 直接编写 Plugin 是 OK 的,那么为了复用性更高一些,那我们怎么把这个抽出去呢?


如下



其中 build.gradle 为


apply plugin: 'groovy'apply plugin: 'maven'repositories {mavenLocal()jcenter()}


dependencies {compile gradleApi()}


def versionName = "0.0.1"group "com.ding.demo"version versionNameuploadArchives{ //当前项目可以发布到本地文件夹中 repositories {mavenDeployer {repository(url: uri('../repo')) //定义本地 maven 仓库的地址}}}


apkname.properties 为


implementation-class=com.ding.demo.ApkChangeNamePlugin


ApkChangeNamePlugin


package com.ding.demo


import org.gradle.api.Projectimport org.gradle.api.Plugin


class ApkChangeNamePlugin implements Plugin<Project>{


static class ChangeAppNameConfig{String prefixNameString notConfig}


static def buildTime() {return new Date().format("yyyy_MM_dd_HH_mm_ss", TimeZone.getTimeZone("GMT+8"))}


@Overridevoid apply(Project project) {if(!project.android){throw new IllegalStateException('Must apply 'com.android.application' or 'com.android.library' first!');}project.getExtensions().create("nameConfig",ChangeAppNameConfig)ChangeAppNameConfig configproject.afterEvaluate {config = project.nameConfig}project.android.applicationVariants.all{variant ->variant.outputs.all {output ->if (output.outputFile != null && output.outputFile.name.endsWith('.apk')&& !output.outputFile.name.contains(config.notConfig)) {def appName = config.prefixNamedef time = buildTime()String name = output.baseNamename = name.replaceAll("-", "_")outputFileName = "{variant.versionCode}-{time}.apk"}}}}}


定义完成后,执行./gradlew uploadArchives 会在本目录生成对应对应的插件



应用插件 在根 build.gralde 配置


buildscript {repositories {maven {url uri('./repo/')}google()jcenter()}dependencies {classpath 'com.android.tools.build:gradle:3.4.1'classpath 'com.ding.demo:apkname:0.0.1'}}


在 app.gralde 设置


apply plugin: 'apkname'nameConfig{prefixName = 'demo'notConfig = 'debug'}

Gradle doc 官网

Gradle 的基础 API 差不多就介绍完了。


官网地址:docs.gradle.org/current/use…


可以去查看对应的 API,也可以直接通过源码的方式查看


但是笔记还没完,学习了 Gradle 的基础,我们要让其为我们服务。下面介绍几个实际应用.

APT 技术

www.jianshu.com/p/94aee6b02…


blog.csdn.net/kaifa1321/a…


APT 全称 Annotation Processing Tool,编译期解析注解,生成代码的一种技术。常用一些 IOC 框架的实现原理都是它,著名的 ButterKnife,Dagger2 就是用此技术实现的,SpringBoot 中一些注入也是使用他进行注入的.


在介绍 APT 之前,先介绍一下 SPI (Service Provider Interface)它通过在 ClassPath 路径下的 META-INF/**文件夹查找文件,自动加载文件里所定义的类。 上面自定义的 ApkNamePlugin 就是使用这种机制实现的,如下.



SPI 技术也有人用在了组件化的过程中进行解耦合。


要实现一个 APT 也是需要这种技术实现,但是谷歌已经把这个使用 APT 技术重新定义了一个,定义了一个 auto-service,可以简化实现,下面就实现一个简单 Utils 的文档生成工具。

Utils 文档生成插件

我们知道,项目中的 Utils 可能会很多,每当新人入职或者老员工也不能完成知道都有那些 Utils 了,可能会重复加入一些 Utils,比如获取屏幕的密度,框高有很多 Utils.我们通过一个小插件来生成一个文档,当用 Utils 可以看一眼文档就很一目了然了.

新建一个名为 DocAnnotation 的 Java Libary

定义一个注解


@Retention(RetentionPolicy.CLASS)public @interface GDoc {String name() default "";


String author() default "";


String time() default "";}

新建一个名为 DocComplie 的 Java Libary 先

然后引入谷歌的 auto-service,引入 DocAnnotation


apply plugin: 'java-library'dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'com.google.auto.service:auto-service:1.0-rc2'implementation 'com.alibaba:fastjson:1.2.34'implementation project(':DocAnnotation')}


定义一个 Entity 类


public class Entity {


public String author;public String time;public String name;}


定义注解处理器


@AutoService(Processor.class) //其中这个注解就是 auto-service 提供的 SPI 功能 public class DocProcessor extends AbstractProcessor{


Writer docWriter;


@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);


}


@Overridepublic Set<String> getSupportedAnnotationTypes() {//可处理的注解的集合 HashSet<String> annotations = new HashSet<>();String canonicalName = GDoc.class.getCanonicalName();annotations.add(canonicalName);return annotations;}


@Overridepublic SourceVersion getSupportedSourceVersion() {return SourceVersion.latestSupported();}


@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {Messager messager = processingEnv.getMessager();Map<String,Entity> map = new HashMap<>();StringBuilder stringBuilder = new StringBuilder();for (Element e : env.getElementsAnnotatedWith(GDoc.class)) {GDoc annotation = e.getAnnotation(GDoc.class);Entity entity = new Entity();entity.name = annotation.name();entity.author = annotation.author();entity.time = annotation.time();map.put(e.getSimpleName().toString(),entity);


stringBuilder.append(e.getSimpleName()).append(" ").append(entity.name).append("\n");}


try {docWriter = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT,"","DescClassDoc.json").openWriter();

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android Gradle 学习笔记整理,阿里Android面试必问