• 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对应输出目录如appuildgeneratedsource elease,packageForR为包名,如com.example.pengf.myapplication,则R.java的输出位置为appuildgeneratedsource eleasecomexamplepengfmyapplicationR.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;
      }

     

  • 相关阅读:
    以最简单的方式讲HashMap
    Python获得百度统计API的数据并发送邮件
    Java反射,注解,以及动态代理
    LeetCode每天一题之两数之和
    记一次SSM项目小结(一)
    OpenStack配置串口显示虚机界面
    sqlalchemy外键和relationship查询
    sqlalchemy数据库分层操作
    数据库外键基础知识和操作(世界杯版)
    openstack ovs实现vlan组网
  • 原文地址:https://www.cnblogs.com/rainboy2010/p/9235645.html
Copyright © 2020-2023  润新知