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 ...
随机推荐
- 【学亮编程手记】Spring Cloud三大组件Eureka/Feign/Histrix的原理及使用
- python 三元运算符、推导式、递归、匿名函数、内置函数
三目运算符 # 三目(元)运算符:就是 if...else...语法糖 # 前提:简化if...else...结构,且两个分支有且只有一条语句 # 注:三元运算符的结果不一定要与条件直接性关系 cmd ...
- C# call webservice方法
https://www.cnblogs.com/Fooo/p/5507153.html
- 清北学堂(2019 4 28 ) part 1
今天主要用来铺路,打基础 枚举 没什么具体算法讲究,但要考虑更优的暴力枚举方法,例如回文质数,有以下几种思路: 1.挨个枚举自然数,再一起判断是否是回文数和质数,然而一看就不是最优 2.先枚举质数再判 ...
- 简单实现SSO
方案一:原理:基于SSO Server 端的登录情况,跳转至SOO-client的各个端. 每次返回一个 ticker 随机票据值识别. 配置服务端 执行 :git clone https://git ...
- [BJOI2019]奥术神杖(分数规划,动态规划,AC自动机)
[BJOI2019]奥术神杖(分数规划,动态规划,AC自动机) 题面 洛谷 题解 首先乘法取\(log\)变加法,开\(c\)次根变成除\(c\). 于是问题等价于最大化\(\displaystyle ...
- [SHOI2007]善意的投票
题目描述 幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉.对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神.虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来 ...
- 快速理解js中的call,apply的作用
今天被人问到js中的call,apply的区别和用途,解释了一番后,想到之前在逼乎上看到一位小伙伴生动形象的解释 本身不难理解,看下MDN就知道了,但是不常用,遇到了,还要脑回路回转下.或者时间长了, ...
- codeforces-1133 (div3)
A.先全部化成分钟数,取平均数之后化成正常时刻. #include <map> #include <set> #include <ctime> #include & ...
- 野路子码农系列(3)plotly可视化的简单套路
又双叒叕要跟客户汇报了,图都准备好了吗?matplotlib出图嫌丑?那用用plotly吧,让你的图看上去经费爆炸~ P1 起因 第一次接触plotly这个库是在我们做的一个列车信号数据挖掘的项目里, ...