在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. JS中的事件委托(事件代理)

    一步一步来说说事件委托(或者有的资料叫事件代理) js中事件冒泡我们知道,子元素身上的事件会冒泡到父元素身上. 事件代理就是,本来加在子元素身上的事件,加在了其父级身上. 那就产生了问题:父级那么多子 ...

  2. Java的String和StringBuilder

    一.String 1.创建String对象的方法: String s1="haha"; String s2=new String(); String s3=new String(& ...

  3. golang项目练习

    一.记账系统 1.该软件能够记录收入.支出,并能够打印收支明细表 2. 代码 package main import ( . "fmt" ) func menu() string{ ...

  4. 南邮 base64全家桶

    这几天不想学逆向 做做crypto(菜还瞎j2做)..... 题目: 全家桶全家桶全家桶!我怎么饿了......密文(解密前删除回车):R1pDVE1NWlhHUTNETU4yQ0dZWkRNTUpY ...

  5. tensorflow-TFRecord报错ValueError: Protocol message Feature has no "feature" field.

    编写代码用TFRecord数据结构存储数据集信息是报错:ValueError: Protocol message Feature has no "feature" field.或和 ...

  6. Linux下MySql的登陆和管理操作

    一.mysql数据库启停1.linux下启动mysql的命令:    mysqladmin start/ect/init.d/mysql start (前面为mysql的安装路径)2.linux下重启 ...

  7. php Header 函数使用

    <?php header('HTTP/1.1 200 OK'); // ok 正常访问 header('HTTP/1.1 404 Not Found'); //通知浏览器 页面不存在 heade ...

  8. echart在X轴下方添加字

    使用Echart做统计图表,这个方便快捷还高大上 官方网址 https://www.echartsjs.com/ 按照文档,很快就做出了一个柱图表 在X轴下方,要显示出对应日期是星期几(上图最下方,用 ...

  9. 题解:[JSOI2004]平衡点 / 吊打XXX

    这个题目算是一个模拟退火的板子题 物重一定,绳子越短,重物越低,势能越小,势能又与物重成正比 使得$\sum_{i=1}^nd[i]*w[i]$也就是总的重力势能最小,可以使得系统平衡 交了两面半.. ...

  10. POJ1179Polygon(区间dp)

    啊~~ 被dp摁在地上摩擦的人 今天做了一道区间dp的题(POJ1179Polygon) 题目: Polygon Time Limit: 1000MS   Memory Limit: 10000K T ...