在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插件的更多相关文章

  1. AS 自定义 Gradle plugin 插件 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. 通过Gradle Plugin实现Git Hooks检测机制

    背景 项目组多人协作进行项目开发时,经常遇到如下情况:如Git Commit信息混乱,又如提交者信息用了自己非公司的私人邮箱等等.因此,有必要在Git操作过程中的适当时间点上,进行必要的如统一规范.安 ...

  3. AS Gradle构建工具与Android plugin插件【大全】

    Android plugin version 与 gradle version 的关系 Gradle是一种构建工具,它通过编写一个名为build.gradle的脚本文件对项目进行设置,再根据这个脚本对 ...

  4. gradle ssh 插件

    org.hidetake.ssh Gradle SSH Plugin is a Gradle plugin which provides remote command execution and fi ...

  5. 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 ...

  6. Gradle 自定义插件

    使用版本 5.6.2 插件被用来封装构建逻辑和一些通用配置.将可重复使用的构建逻辑和默认约定封装到插件里,以便于其他项目使用. 你可以使用你喜欢的语言开发插件,但是最终是要编译成字节码在 JVM 运行 ...

  7. 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 ...

  8. gradle/gradle plugin/Android studio关系

    gradle - 构建工具,存储于Users/stono/.gradle/wrapper/dists Adroid Studio- IDE Gradle plugin - 在AS中使用Gradle的插 ...

  9. Gradle之Android Gradle Plugin 主要流程分析(二)

    [Android 修炼手册]Gradle 篇 -- Android Gradle Plugin 主要流程分析 预备知识 理解 gradle 的基本开发 了解 gradle task 和 plugin ...

  10. android gradle 和gradle plugin

    android gradle 和gradle plugin 1.安装完AS3.5.2创建完项目一运行,报了如下错误 Error:Could not find com.android.tools.bui ...

随机推荐

  1. Vue.js 2.x笔记:安装与起步(1)

    1. 环境准备 Vue是一套用于构建用户界面的渐进式框架,设计为可以自底向上逐层应用.Vue 的核心库只关注视图层. 安装Node.js,下载:https://nodejs.org/ 查看安装: $ ...

  2. sax

    <?xml version="1.0" encoding="UTF-8"?> <beauties> <beauty> < ...

  3. HTML事件属性

    1.常用窗口事件属性 属性 值 描述 onbeforeonload script 在文档加载之前运行脚本 onblur script 当窗口失去焦点时运行脚本 onerror script 当错误发生 ...

  4. Java面试题之高级篇研读

    1.List和Set比较,各自的子类比较 对比一:ArrayList与LinkedList比较 1.ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较 ...

  5. Django 对接 支付宝支付, 回调

    平台 点击这里进入 蚂蚁金服开放平台 沙箱 点击这里进入 沙箱环境 初始界面 设置公钥 下载创建秘钥工具 1.  进入文档中心 这里 2. 选中 电脑网站支付 3. 进入后选中 API 列表 中的 统 ...

  6. [HNOI2009]最小圈(分数规划+SPFA判负环)

    题解:求环长比环边个数的最小值,即求min{Σw[i]/|S|},其中i∈S.这题一眼二分,然后可以把边的个数进行转化,假设存在Σw[i]/|S|<=k,则Σw[i]-k|S|<=0,即Σ ...

  7. easyUI文本框获得焦点,失去焦点

    easyUI帮助文档对于文本框的操作没有提供直接获得焦点,或者失去焦点的方法,我们可以采用以下写法来实现. 获得焦点: $('input',$('#文本框Id').next('span')).focu ...

  8. JavaEEMybatis基础整理

    一:对原生态JDBC问题的总结 新项目要使用mybatis作为持久层框架,由于本人之前一直使用的Hibernate,对mybatis的用法实在欠缺,最近几天计划把mybatis学习一哈,特将学习笔记记 ...

  9. 野路子码农系列(1) 创建Web API

    新工作正式开始了2天,由于客户暂时还没交接数据过来,暂时无事可做.恰逢政佬给某超市做的商品图像识别的项目客户催收了,老板要求赶紧搞个API,于是我就想我来试试吧. 说起API,我其实是一窍不通的,我对 ...

  10. Linux下的sudo及配置

    sudo的常用命令 man sudoers # 参阅帮助 visudo # 编辑sudoers文件的命令 sudo -l # 查看可执行或禁止执行的命令 sudo -u user1 /bin/ls # ...