抛出问题:

 

1.Butterknife是干什么的?

2.我们如何去看Butterknife框架

3.如果我们自己写一个类似Butterknife框架,我们如何去做?

 

ButterKnife是一个专注于Android系统的View注入框架,可以减少大量的findViewById  以及 setOnClickListener代码,可视化一键生成。

 

解决问题:

2.首先我们要明白 butterknife-annotations  ,butterknife-compiler是干什么的?

butterknife 非androidx支持 android butterknife原理_类名

 

我们从butterknife-annotations点击发现:

 

 


 


import android.support.annotation.IdRes;
        import java.lang.annotation.Retention;
        import java.lang.annotation.Target;

        import static java.lang.annotation.ElementType.FIELD;
        import static java.lang.annotation.RetentionPolicy.CLASS;

/**
 * Bind a field to the view for the specified ID. The view will automatically be cast to the field
 * type.
 * <pre><code>
 * {@literal @}BindView(R.id.title) TextView title;
 * </code></pre>
 */
@Retention(CLASS) @Target(FIELD)
public @interface BindView {
    /** View ID to which the field will be bound. */
    @IdRes int value();
}

这里的注解BideView我们会在我们的Activity 绑定控件使用到 eg:


butterknife 非androidx支持 android butterknife原理_java_02

 

 

 

 

butterknife-compiler点击我们会发现:

butterknife 非androidx支持 android butterknife原理_类名_03

 

 下面我们就具体解释他们的作用:

 

 

作用

AbstractProcessor / Annotation Processor Tool  (apt)

是javac的一个工具,用来在编译时扫描和编译和处理注解(Annotation)。你可以自己定义注解和注解处理器去搞一些事情。一个注解处理器它以Java代码或者(编译过的字节码)作为输入,生成文件(通常是java文件)。这些生成的java文件不能修改,并且会同其手动编写的java代码一样会被javac编译。

RetentionPolicy.SOURCE

编译之后抛弃,存活的时间在源码和编译时

RetentionPolicy.CLASS

保留在编译后class文件中,但JVM将会忽略(对于 底层系统开发-开发编译过程 使用)    apt中source和class都一样

RetentionPolicy.RUNTIME

能够使用反射读取和使用(运行时)

 

3.butterknife使用

 

配置

(1).在工程目录下  的build.gradle    添加apt插件classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

(2). 在app  目录下的build.gradle  应用 添加apt插件apply plugin: 'com.neenbedankt.android-apt'

(3).依赖 具有apt 处理的moudle    这里不是compile  而是aptapt project(':butterknife-compiler')

 

二.具体应用

1.创建模块butterknife-annotion

butterknife 非androidx支持 android butterknife原理_类名_04

 

 

2.创建模块注解处理器butterkniffe-complier

butterknife 非androidx支持 android butterknife原理_ButterKnife_05

 

 

注解处理器的作用上面我们已经说过了 就是处理注解生成Java文件   和javapoet类似

 

 

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.dongnao.butterknife_annotion.BindView")
public class ButterKniffeProcess extends AbstractProcessor {

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(BindView.class.getCanonicalName());
        return types;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

 

butterknife 非androidx支持 android butterknife原理_java_06

 

 

 

@Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//
        System.out.println("-----------------process-----------------------");
        //VariableElement     成员遍历
        Set<? extends Element> elementSet = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        //  区分  key是activity  全类名=包名+类名   值   成员变量集合
        Map<String, List<VariableElement>> cacheMap = new HashMap<>();
        //MainActivity.class   标签
        for (Element element : elementSet) {
        // 全类名=包名+类名
            VariableElement variableElement = (VariableElement) element;
            String activityName = getActivityName(variableElement);

            List<VariableElement> list = cacheMap.get(activityName);
            if (list == null) {
                list = new ArrayList<>();
                cacheMap.put(activityName, list);
            }
//            分类
            list.add(variableElement);
            System.out.println("--------->" + variableElement.getSimpleName().toString());
        }
//  2  为每一个activity生成  Class
//
        Iterator iterator = cacheMap.keySet().iterator();
        while (iterator.hasNext()) {
            //得到mainActivity名字
            String activityName = (String) iterator.next();
            //得到mainActivity  控件成员变量的集合
            List<VariableElement> caheElements = cacheMap.get(activityName);

            String newActivityBinder = activityName + "$ViewBinder";
            Filer filer = processingEnv.getFiler();
            try {
                JavaFileObject javaFileObject = filer.createSourceFile(newActivityBinder);

                String packageName = getPackageName(caheElements.get(0));
                Writer writer = javaFileObject.openWriter();
                String activitySimpleName = caheElements.get(0).getEnclosingElement().getSimpleName().toString() + "$ViewBinder";

                writeHeader(writer, packageName, activityName, activitySimpleName);

//中间部分
                for (VariableElement variableElement : caheElements) {
                    BindView bindView = variableElement.getAnnotation(BindView.class);
                    int id = bindView.value();
                    String fieldName = variableElement.getSimpleName().toString();
                    TypeMirror typeMirror = variableElement.asType();
//                    TextView
                    writer.write("target." + fieldName + "=(" + typeMirror.toString() + ")target.findViewById(" + id + ");");
                    writer.write("\n");
                }


                //结尾部分
                writer.write("\n");
                writer.write("}");
                writer.write("\n");
                writer.write("}");
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }


        }

        return false;
    }



Element:
//获得父节点 (类)
    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

上面的代码其实是解析注解生成如下代码:

 

butterknife 非androidx支持 android butterknife原理_类名_07

 

package com.example;    // PackageElement    

获取包名processingEnv.getElementUtils().getPackageOf(typeElement).getQualifiedName().toString();


public class Dog{        // TypeElement   获取类名

     private int age;      // VariableElement  获取属性

     public Dog() {}    // ExecuteableElement

     public void setA ( ) {} // ExecuteableElement 
}
 
 
-----------------process-----------------------
 -------packageName--------com.butterknifeframwork
 --------->test
 -------packageName--------com.butterknifeframwork
 --------->textView
 -------packageName--------com.butterknifeframwork
 --------->textView
 -------packageName--------com.butterknifeframwork
 -------packageName--------com.butterknifeframwork
 -------packageName--------com.butterknifeframwork

 

3.测试:绑定成功

 

 

butterknife 非androidx支持 android butterknife原理_java_08