ButterKnife源码分析和手写

ButterKnife采用编译时注解,就是采用apt动态生成代码?

第一步:热身准备: 先要创建三个java library , 分别是 butterknife, butterknife-annotation, butterknife-compile

第二步:使用apt 动态生成,需要的gradle 配置

1. 在butterknife-compiler 的 build.gradle 中的

dependencies {
     //apt 的依赖
    compile 'com.squareup:javapoet:1.9.0'
     //谷歌的一个库
    compile 'com.google.auto.service:auto-service:1.0-rc2'
}

2.  在 build.gradle 中添加    mavenCentral()

3. 添加一些必要的配置

/**
 * 解决 .addModifiers(Modifier.FINAL, Modifier.PUBLIC);报错的问题
 * Gradle <= 2.2
 * 1.App的build.gradle 中 apt project(':butterknife-compiler')
 *    在 最上面加上 apply plugin: 'com.neenbedankt.android-apt'
 * 2.在全局的 build.gradle repositories和 中 添加 mavenCentral()

第三步, 我们需要 在butterknife-annotation 创建 一个BindView注解

扫描二维码关注公众号,回复: 4986270 查看本文章
// SOURCE(.java) < CLASS(.java .class ) < RUNTIME(.java .class JVM)
//SOURCE 进行 @Override @SuppressWarning 进行这样的检查的时候使用
//CLASS 进行一些预处理 比如动态生成辅助代码
//RUNTIME 需要在运行时动态获取注解信息的。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)  //编译时,
public @interface BindView {
    int value();
}

第四步,在 butterknife-compiler中创建下类

@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {

    private Filer mFiler;
    private Elements mElementUtils;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mFiler = processingEnvironment.getFiler();
        mElementUtils = processingEnvironment.getElementUtils();
    }

    //1. 指定处理的版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        //返回最高的版本
        System.out.println("返回最高的版本"+SourceVersion.latestSupported());
        return SourceVersion.latestSupported();
    }

    //给定需要处理的注解


    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
            //annotation.getCanonicalName() 返回的是 com.butterknif_annotation.BindView(全路径)
            types.add(annotation.getCanonicalName());
        }
        return types;
    }

    /**
     * 参考了 ButterKnife 的写法
     *
     * @return
     */
    private Set<Class<? extends Annotation>> getSupportedAnnotations() {
        Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
        //将所有带注解的参数 加入到集合中
        annotations.add(BindView.class);
        return annotations;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//        System.out.println("----------------------process----------------");
//        System.out.println("----------------------process----------------");
//        System.out.println("----------------------process----------------");
//        System.out.println("----------------------process----------------");

        //process 方法代表的是, 有注解就会进来,但是这里面是一团乱麻
        //从所有的类中 找到带该参数的 变量
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
//        for (Element element : elements) {
//            //System.out.println("----------------------process----------------"+element.getSimpleName().toString()); //textView
//            Element enclosingElement = element.getEnclosingElement();  //com.mybutterknife.MainActivity
//            //System.out.println("----------------------process+enclosingElement----------------"+enclosingElement.toString());
//
//        }

        //解析 属性activity -- list
        Map<Element, List<Element>> elementMap = new LinkedHashMap<>();
        for (Element element : elements) { //element 需要动态注册的变量
            Element enclosingElement = element.getEnclosingElement(); // 类 key
            List<Element> viewBindListElements = elementMap.get(enclosingElement); // value 第一次取为空
            if (viewBindListElements == null){
                viewBindListElements = new ArrayList<>();
                elementMap.put(enclosingElement, viewBindListElements);
            }
            viewBindListElements.add(element);
        }

        //生成代码  entrySet(key+value) keySet(key)
        for (Map.Entry<Element, List<Element>> entry : elementMap.entrySet()) {
            Element key = entry.getKey();
            List<Element> value = entry.getValue();

            /**
             * 解决 .addModifiers(Modifier.FINAL, Modifier.PUBLIC);报错的问题
             * Gradle <= 2.2
             * 1.App的build.gradle 中 apt project(':butterknife-compiler')
             *    在 最上面加上 apply plugin: 'com.neenbedankt.android-apt'
             * 2.在全局的 build.gradle repositories和 中 添加 mavenCentral()
             *   已经加上apt的版本: classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
             *   Gradle >= 2.2
             *       annotationProcessor
             */

            //构造类
            String activityClassNameStr = key.getSimpleName().toString();
            //将一个 String 类型 转成 Object
            ClassName activityClassName = ClassName.bestGuess(activityClassNameStr);
            ClassName unbindClassName = ClassName.get("com.butterknife", "Unbinder");
            TypeSpec.Builder classBuilder = TypeSpec.classBuilder(activityClassNameStr+"_ViewBinding")
                    .addModifiers(Modifier.FINAL, Modifier.PUBLIC).addSuperinterface(unbindClassName);

            //构造属性
            classBuilder.addField(activityClassName, "target", Modifier.PRIVATE);

            //普通方法
            ClassName callSuperClassName = ClassName.get("android.support.annotation", "CallSuper");
            MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("unbind")
                    .addAnnotation(Override.class)
                    .addAnnotation(callSuperClassName)
                    .addModifiers(Modifier.PUBLIC);

            unbindMethodBuilder.addStatement("$L target = this.target", activityClassName);


            //构造函数
            ClassName uiThreadClassName = ClassName.get("android.support.annotation", "UiThread");
            MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder()
                    .addAnnotation(uiThreadClassName)
                    .addModifiers(Modifier.PUBLIC)
                    .addParameter(activityClassName, "target");

            constructorMethodBuilder.addStatement("this.target = target");

            //findViewById 属性
            for (Element element : value) {
                String fieldName = element.getSimpleName().toString();
                ClassName utilsClassName = ClassName.get("com.mybutterknife", "Utils");
                int resId = element.getAnnotation(BindView.class).value();
                constructorMethodBuilder.addStatement("target.$L = $T.findViewById(target,$L)", fieldName, utilsClassName, resId);

                unbindMethodBuilder.addStatement("target.$L = null", fieldName);
            }




            classBuilder.addMethod(unbindMethodBuilder.build());
            classBuilder.addMethod(constructorMethodBuilder.build());
            //生成类
            try {
                String packageName = mElementUtils.getPackageOf(key).getQualifiedName().toString();
                JavaFile.builder(packageName, classBuilder.build())
                         .addFileComment("butterknife 自动生成")
                         .build().writeTo(mFiler);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("翻车了");
            }
        }
        return false;
    }
}

需要注意的是

1.该类要继承   AbstractProcessor  并且 覆写 这个方法   process 在该方法中 动态创建类 。

2.在该类上添加注解    @AutoService(Processor.class)

3.process 方法默认返回 false 就可以了

第五步 在butterkenife中创建如下类 ,并在类中 通过反射 调用 通过apt动态生成的代码 。

/**
 * person.getClass().getName():  AnnotationJava.test.Person
 * person.getClass().getSimpleName():  Person
 * person.getClass().getCanonicalName():  AnnotationJava.test.Person
 */
public class ButterKnife {

    public static Unbinder bind(Activity activity){

        try {
            Class<? extends Unbinder> bindClassName = (Class<? extends Unbinder>) Class.forName(activity.getClass().getName() + "_ViewBinding");

            Constructor<? extends Unbinder> constructor = bindClassName.getDeclaredConstructor(activity.getClass());

            Unbinder unbinder = constructor.newInstance(activity);

            return unbinder;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return Unbinder.EMPTY;
    }
}

第六步, 需要特别注意的是 ,动态生成类需要是要如下接口

public interface Unbinder {
    @UiThread
    void unbind();

    Unbinder EMPTY = new Unbinder() {
        @Override
        public void unbind() {
        }
    };
}

至于apt的api 可以参考如下网址

https://blog.csdn.net/xuguobiao/article/details/72775730

关于面试

 
 
 
 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/lyfxh1314/article/details/84864111
今日推荐