解析butterknife原理及手写实现

抛出问题:

 

1.Butterknife是干什么的?

2.我们如何去看Butterknife框架

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

 

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

 

解决问题:

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

 

我们从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-compiler点击我们会发现:

 

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

 

 作用
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

 

 

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

 

 

注解处理器的作用上面我们已经说过了 就是处理注解生成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();
    }

 

 

 

 

  @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();
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.测试:绑定成功

 

 

源码点击下载

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值