butterknife-gradle-plugin插件
在android library项目里由于R类中变量不再是final类型而无法使用butterknife,为了解决此问题,Jakewharton大神引入了butterknife-gradle-plugin插件,用于生成变量类型为final的R2类。
此处为butterknife-gradle-plugin 8.4.0版本为例,介绍一下插件在library中的使用以及源码分析。
使用
在项目的build.gradle中添加classpath:
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0'
}
}
在library项目build.gradle中引入插件:
apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
在library项目build.gradle中添加依赖:
implementation 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
源码分析
插件主要有两个文件ButterKnifePlugin.groovy和FinalRClassBuilder.java
ButterKnifePlugin.groovy
public class ButterKnifePlugin implements Plugin<Project> {
@Override
void apply(Project project) {
if (!(project.plugins.hasPlugin(LibraryPlugin) || project.plugins.hasPlugin(AppPlugin))) {
throw new IllegalStateException('Butterknife plugin can only be applied to android projects')
}
def variants
if (project.plugins.hasPlugin(LibraryPlugin)) {
variants = project.android.libraryVariants
} else {
variants = project.android.applicationVariants
}
project.afterEvaluate {
variants.all { BaseVariant variant ->
variant.outputs.each { BaseVariantOutput output ->
output.processResources.doLast {
File rDir = new File(sourceOutputDir, packageForR.replaceAll('\\.',
StringEscapeUtils.escapeJava(File.separator)))
File R = new File(rDir, 'R.java')
FinalRClassBuilder.brewJava(R, sourceOutputDir, packageForR, 'R2')
}
}
}
}
}
}
在apply方法里首先判断项目是否存在LibraryPlugin(com.android.library)或AppPlugin(com.android.application),如不存在则抛出异常,然后获取对应的variants,在项目的processResources阶段,获取R.java文件的信息,sourceOutputDir对应输出目录如app\build\generated\source\r\release,packageForR为包名,如com.example.pengf.myapplication,则R.java的输出位置为app\build\generated\source\r\release\com\example\pengf\myapplication\R.java,然后调用FinalRClassBuilder类的brewJava方法生成R2.java文件
FinalRClassBuilder.java
用于根据R.java文件生成R2.java文件
public final class FinalRClassBuilder {
private static final String SUPPORT_ANNOTATION_PACKAGE = "android.support.annotation";
private static final String[] SUPPORTED_TYPES = {
"array", "attr", "bool", "color", "dimen", "drawable", "id", "integer", "string"
};
private FinalRClassBuilder() {
}
public static void brewJava(File rFile, File outputDir, String packageName, String className)
throws Exception {
CompilationUnit compilationUnit = JavaParser.parse(rFile);
TypeDeclaration resourceClass = compilationUnit.getTypes().get(0);
TypeSpec.Builder result =
TypeSpec.classBuilder(className).addModifiers(PUBLIC).addModifiers(FINAL);
for (Node node : resourceClass.getChildrenNodes()) {
if (node instanceof TypeDeclaration) {
addResourceType(Arrays.asList(SUPPORTED_TYPES), result, (TypeDeclaration) node);
}
}
JavaFile finalR = JavaFile.builder(packageName, result.build())
.addFileComment("Generated code from Butter Knife gradle plugin. Do not modify!")
.build();
finalR.writeTo(outputDir);
}
private static void addResourceType(List<String> supportedTypes, TypeSpec.Builder result,
TypeDeclaration node) {
if (!supportedTypes.contains(node.getName())) {
return;
}
String type = node.getName();
TypeSpec.Builder resourceType = TypeSpec.classBuilder(type).addModifiers(PUBLIC, STATIC, FINAL);
for (BodyDeclaration field : node.getMembers()) {
if (field instanceof FieldDeclaration) {
addResourceField(resourceType, ((FieldDeclaration) field).getVariables().get(0),
getSupportAnnotationClass(type));
}
}
result.addType(resourceType.build());
}
private static void addResourceField(TypeSpec.Builder resourceType, VariableDeclarator variable,
ClassName annotation) {
String fieldName = variable.getId().getName();
String fieldValue = variable.getInit().toString();
FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(int.class, fieldName)
.addModifiers(PUBLIC, STATIC, FINAL)
.initializer(fieldValue);
if (annotation != null) {
fieldSpecBuilder.addAnnotation(annotation);
}
resourceType.addField(fieldSpecBuilder.build());
}
private static ClassName getSupportAnnotationClass(String type) {
return ClassName.get(SUPPORT_ANNOTATION_PACKAGE, capitalize(type) + "Res");
}
private static String capitalize(String word) {
return Character.toUpperCase(word.charAt(0)) + word.substring(1);
}
}
brewJava方法主要是调用javapoet生成R2.java文件,支持的类型有array, attr, bool, color, dimen, drawable, id, integer, string,首先通过JavaParser类对R.java文件进行转换,然后依次读入R.java中每个节点,如该节点的类型为支持的类型,则将该节点下面的每个变量都写入到R2.java中,变量前加入final关键字,值为R.java中变量对应的值,同时为每个变量添加注解。最后将R2.java文件写入到指定输出目录。
如R.java里有以下内容:
public static final class bool {
public static int abc_action_bar_embed_tabs = 0x7f050001;
public static int abc_allow_stacked_button_bar = 0x7f050002;
public static int abc_config_actionMenuItemAllCaps = 0x7f050003;
public static int abc_config_showMenuShortcutsWhenKeyboardPresent = 0x7f050004;
}
则生成的R2.java文件如下,每一个变量前都加入了final关键字,其值为R.java中对应的值,还加入了android.support.annotation类中对应的注解
public static final class bool {
@BoolRes
public static final int abc_action_bar_embed_tabs = 0x7f050001;
@BoolRes
public static final int abc_allow_stacked_button_bar = 0x7f050002;
@BoolRes
public static final int abc_config_actionMenuItemAllCaps = 0x7f050003;
@BoolRes
public static final int abc_config_showMenuShortcutsWhenKeyboardPresent = 0x7f050004;
}
butterknife-gradle-plugin插件的更多相关文章
- AS 自定义 Gradle plugin 插件 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 通过Gradle Plugin实现Git Hooks检测机制
背景 项目组多人协作进行项目开发时,经常遇到如下情况:如Git Commit信息混乱,又如提交者信息用了自己非公司的私人邮箱等等.因此,有必要在Git操作过程中的适当时间点上,进行必要的如统一规范.安 ...
- AS Gradle构建工具与Android plugin插件【大全】
Android plugin version 与 gradle version 的关系 Gradle是一种构建工具,它通过编写一个名为build.gradle的脚本文件对项目进行设置,再根据这个脚本对 ...
- gradle ssh 插件
org.hidetake.ssh Gradle SSH Plugin is a Gradle plugin which provides remote command execution and fi ...
- The android gradle plugin version 2.3.0-beta2 is too old, please update to the latest version.
编译项目的时候,报如下错误: Error:(, ) A problem occurred evaluating project ':app'. > Failed to apply plugin ...
- Gradle 自定义插件
使用版本 5.6.2 插件被用来封装构建逻辑和一些通用配置.将可重复使用的构建逻辑和默认约定封装到插件里,以便于其他项目使用. 你可以使用你喜欢的语言开发插件,但是最终是要编译成字节码在 JVM 运行 ...
- Configuration on demand is not supported by the current version of the Android Gradle plugin since you are using Gradle version 4.6 or above. Suggestion: disable configuration on demand by setting org
androidStudio打开cocos3.17.2Lua项目时,出现了 Configuration on demand is not supported by the current version ...
- gradle/gradle plugin/Android studio关系
gradle - 构建工具,存储于Users/stono/.gradle/wrapper/dists Adroid Studio- IDE Gradle plugin - 在AS中使用Gradle的插 ...
- Gradle之Android Gradle Plugin 主要流程分析(二)
[Android 修炼手册]Gradle 篇 -- Android Gradle Plugin 主要流程分析 预备知识 理解 gradle 的基本开发 了解 gradle task 和 plugin ...
- android gradle 和gradle plugin
android gradle 和gradle plugin 1.安装完AS3.5.2创建完项目一运行,报了如下错误 Error:Could not find com.android.tools.bui ...
随机推荐
- [转帖]流程控制:for 循环
流程控制:for 循环 http://wiki.jikexueyuan.com/project/linux-command/chap34.html need more study need more ...
- Struts2+Spring+Hibernate3整合
这几天正在复习三大框架的知识,特意把写出来,如有错误,希望大家多指教! 代码地址:https://git.coding.net/puchenglin/SSHDemo.git 1. 引入jar包 Str ...
- vue中怎么全局引入sass文件
1.添加依赖 npm install sass-resources-loader --save-dev 2.修改build/utils.js scss: generateLoaders('sass') ...
- Quill 富文本编辑器
Quill 富文本编辑器 https://quilljs.com/ https://github.com/quilljs/quill https://github.com/quilljs/awesom ...
- css的寬高約束
https://blog.csdn.net/qq_26780317/article/details/80736514
- Centos7 安装gitLab
我这里使用的是centos 7 64bit,我试过centos 6也是可以的! 1. 安装依赖软件 yum -y install policycoreutils openssh-server open ...
- Oracle外部表与SQLLDR
两种方法建立外部表 在建表语句中把EXTERNAL_TABLE参数设定为SQLLDR: 从12C起,可以使用模式来运行SQLLDR: 在建表语句中把EXTERNAL_TABLE参数设定为SQLLDR: ...
- BZOJ 1855 股票交易 (算竞进阶习题)
单调队列优化dp dp真的是难..不看题解完全不知道状态转移方程QAQ 推出方程后发现是关于j,k独立的多项式,所以可以单调队列优化.. #include <bits/stdc++.h> ...
- P2522 [HAOI2011]Problem b (莫比乌斯反演)
题目 P2522 [HAOI2011]Problem b 解析: 具体推导过程同P3455 [POI2007]ZAP-Queries 不同的是,这个题求的是\(\sum_{i=a}^b\sum_{j= ...
- pytest 13 使用自定义标记mark
前言: pytest可以规定那些要跑,那些不跑,跑特定的哪些?比如以下的这个例子: #!/usr/bin/env/python # -*-coding:utf-8-*- import pytest @ ...