注解处理器->02源码注释

2019-05-31  本文已影响0人  冉桓彬

源码: https://blog.csdn.net/u013045971/article/details/53509237

文档: https://www.jianshu.com/p/9177db78cfed

注解处理器如果说难, 可能只是难在api上面, 由于对api的不熟, 而且网上也不太容易搜索相关的答案, 所以可能觉得难.
切记:不要背api, 如果工作中涉及到需要自己去写相关代码, 只需要记得哪里有相关的实现, 然后直接复制粘贴拿来使用即可.
下面对网上别人写的现成的demo进行注释, 完成之后打算进阶对ButterKnife以及ARouter注解处理器相关源码进行注释.

一、AptActivity

public class AptActivity extends Activity {
    @BindView(R.id.btn1)
    public Button btn1;
    @BindView(R.id.btn2)
    public Button btn2;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_apt);
        ButterKnife.bind(this);
        btn1.setText("按钮1");
        btn2.setText("按钮2");
    }

    @OnClick({R.id.btn1})
    public void click1() {
        Log.v("AndroidTest", "btn1-click");
    }

    @OnClick({R.id.btn2})
    public void click2() {
        Log.v("AndroidTest", "btn2-click");
    }
}

二、AptActivity$$Injector这段代码在查看AnnotatedClass代码时再来对照的查看

public class AptActivity$$Injector implements Injector<AptActivity> {
    @Override
    public void inject(final AptActivity host, Object source, Finder finder) {
        host.btn1=(Button)finder.findView(source, 2131427463);
        host.btn2=(Button)finder.findView(source, 2131427464);
        View.OnClickListener listener;
        listener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                host.click1();
            }
        };
        finder.findView(source, 2131427463).setOnClickListener(listener);
        listener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                host.click2();
            }
        };
        finder.findView(source, 2131427464).setOnClickListener(listener);
    }
}

三、AbstractProcessor

@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
    // 文件相关的辅助类
    private Filer    filer;
    // 元素相关的辅助类
    private Elements elementUtils;
    // 日志相关的辅助类
    private Messager messager;
    // 解析的目标注解集合
    private Map<String, AnnotatedClass> annotatedClassMap = new HashMap<>();
    private Logger logger;

    // ProcessingEnvironment提供一系列的工具类
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        // 作为工具类, 提供对应的Element.
        elementUtils = processingEnv.getElementUtils();
        // 用于编译时日志的输出.
        messager = processingEnv.getMessager();
        // 文件相关的辅助类, 后边生成对应的java文件时需要使用到.
        filer = processingEnv.getFiler();
        logger = new Logger(messager);
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        logger.info("BindView.class.getCanonicalName():" + BindView.class.getCanonicalName());
        logger.info("OnClick.class.getCanonicalName():" + OnClick.class.getCanonicalName());
        Set<String> types = new LinkedHashSet<>();
        types.add(BindView.class.getCanonicalName());
        types.add(OnClick.class.getCanonicalName());
        return types;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
    // RoundEnvironment: 运行环境, 通过该对象可以获取被指定注解标注的元素的集合.
    @Override
    public boolean process(Set<? extends TypeElement> anno, RoundEnvironment roundEnv) {
        annotatedClassMap.clear();
        try {
            // 获取被BindView注解的元素的集合.
            processBindView(roundEnv);
            // 获取被OnClick注解的元素的集合.
            processOnClick(roundEnv);
        } catch (IllegalArgumentException e) {
            return true;
        }

        try {
            for (AnnotatedClass annotatedClass : annotatedClassMap.values()) {
                annotatedClass.setLogger(logger);
                logger.info("annotatedClass:" + annotatedClass + "\nannotatedClass.getFullClassName():" + annotatedClass.getFullClassName());
                JavaFile javaFile = annotatedClass.generateFinder();
                // 这里添加打印然后查看javaFile输出的内容
                logger.info("javaFile:" + javaFile + ",mFiler:" + filer);
                javaFile.writeTo(filer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    private void processBindView(RoundEnvironment roundEnv) {
        // 这里的元素集合就是AptActivity中的btn1与btn2对应的元素:VariableElement.
        for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
            AnnotatedClass annotatedClass = getAnnotatedClass(element);
            BindViewField field = new BindViewField(element);
            logger.info("processBindView()---element:" + element + ",field:" + field);
            annotatedClass.addField(field);
        }
    }

    private AnnotatedClass getAnnotatedClass(Element element) {
        // 1.通过getEnclosingElement可以获取当前Element对应源代码中的父类型元素. 这里指的就是AptActivity
        //   对应的元素类型.
        // 2.只能通过TypeElement获取到对应的类名称, 但是不能获取类信息.
        // 3.如果需要获取类相关的信息, 可以通过typeElement.asType()获取对应的TypeMirror.
        TypeElement encloseElement = (TypeElement) element.getEnclosingElement();
        // 获取对应的类名称.
        String fullClassName = encloseElement.getQualifiedName().toString();
        // 一个fullClassName对应一个AnnotatedClass.
        AnnotatedClass annotatedClass = annotatedClassMap.get(fullClassName);
        if (annotatedClass == null) {
            annotatedClass = new AnnotatedClass(encloseElement, elementUtils);
            annotatedClassMap.put(fullClassName, annotatedClass);
        }
        return annotatedClass;
    }

    private void processOnClick(RoundEnvironment roundEnv) {
        // 这里的元素集合就是AptActivity中的click1()与click2()对应的元素:ExecuteableElement
        for (Element element : roundEnv.getElementsAnnotatedWith(OnClick.class)) {
            AnnotatedClass annotatedClass = getAnnotatedClass(element);
            OnClickMethod method = new OnClickMethod(element);
            annotatedClass.addMethod(method);
        }
    }
}

四、BindViewField

public class BindViewField {
    // VariableElement:表示一个字段、enum常量、方法或构造方法参数、局部变量或异常参数.
    // BindView注解针对的就是变量, 所以这里会将传入的element强转为VariableElement.
    private VariableElement mFieldElement;
    // 获取对应View的id.
    private int mResId;
    public BindViewField(Element element) throws IllegalArgumentException {
        // 判断对应元素的类型是否为FIELD类型.
        if (element.getKind() != ElementKind.FIELD) {
            ...
        }
        mFieldElement = (VariableElement) element;
        BindView bindView = mFieldElement.getAnnotation(BindView.class);
        mResId = bindView.value();
    }
    // 获取被BindView注解的对象的变量名.
    public Name getFieldName() {
        return mFieldElement.getSimpleName();
    }
    public int getResId() {
        return mResId;
    }
    // 因为通过Element无法获取对应元素的具体信息, 所以需要通过asType获取对应的TypeMirror, 然后
    // 根据TypeMirror获取对应的元素信息.
    public TypeMirror getFieldType() {
        return mFieldElement.asType();
    }
}

五、OnClickMethod同BindViewField , 这里不再分析

public class OnClickMethod {
    private Name methodName;
    public int[] ids;
    public OnClickMethod(Element element) {
        if (element.getKind() != ElementKind.METHOD) {
            throw new IllegalArgumentException(String.format("Only method can be annotated width @%s",
                    OnClick.class.getSimpleName()));
        }
        ExecutableElement methodElement = (ExecutableElement) element;
        methodName = methodElement.getSimpleName();
        ids = methodElement.getAnnotation(OnClick.class).value();
        if (ids == null) {
            throw new IllegalArgumentException(String.format("Must set valid ids for @%s", OnClick.class.getSimpleName()));
        } else {
            for (int id : ids) {
                if (id < 0) {
                    throw new IllegalArgumentException(String.format("Must set valid ids for @%s", OnClick.class.getSimpleName()));
                }
            }
        }
        List<? extends VariableElement> parameters = methodElement.getParameters();
        if (parameters.size() > 0) {
            throw new IllegalArgumentException(String.format("The method annotated with @%s must have no parameters", OnClick.class.getSimpleName()));
        }
    }
    public Name getMethodName() {
        return methodName;
    }
}

六、AnnotatedClass

public class AnnotatedClass {
    // 类元素
    public TypeElement         mClassElement;
    // 成员变量: 持有btn1、btn2对应的BindViewField
    public List<BindViewField> mFiled;
    // 方法: 持有click1()、click2()对应的OnClickMethod
    public List<OnClickMethod> mMethod;
    // 元素帮助类
    public Elements            mElementUtils;
    // 在BindViewProcessor中可知, 一个fullClassName对应一个AnnotatedClass.
    public AnnotatedClass(TypeElement classElement, Elements elementUtils) {
        this.mClassElement = classElement;
        this.mElementUtils = elementUtils;
        this.mFiled = new ArrayList<>();
        this.mMethod = new ArrayList<>();
    }

    public String getFullClassName() {
        return mClassElement.getQualifiedName().toString();
    }

    public void addField(BindViewField field) {
        mFiled.add(field);
    }

    public void addMethod(OnClickMethod method) {
        mMethod.add(method);
    }
    // 生成对应的java文件
    public JavaFile generateFinder() {
        /***构建方法*/
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("inject") //方法名称
                .addModifiers(Modifier.PUBLIC) // 方法访问权限
                .addAnnotation(Override.class) // 方法的注解
                .returns(TypeName.VOID) //方法的返回值
                .addParameter(TypeName.get(mClassElement.asType()), "host", Modifier.FINAL) // 方法参数
                .addParameter(TypeName.OBJECT, "source") // 方法参数
                .addParameter(TypeUtil.FINDER, "finder"); // 方法参数
        /*** 遍历添加类成员*/
        for (BindViewField field : mFiled) {
            logger.info("field.getFieldName:" + field.getFieldName() + ", field.getFieldType:" + field.getFieldType());
            // methodBuilder方法内部的执行语句
            methodBuilder.addStatement("host.$N=($T)finder.findView(source,$L)",
                    field.getFieldName(),
                    ClassName.get(field.getFieldType()),
                    field.getResId());
        }
        /*** 声明Listener*/
        if (mMethod.size() > 0) {
            logger.info("TypeUtil.ONCLICK_LISTENER:" + TypeUtil.ONCLICK_LISTENER);
            // methodBuilder方法内部的执行语句
            methodBuilder.addStatement("$T listener", TypeUtil.ONCLICK_LISTENER);
        }

        for (OnClickMethod method : mMethod) {// click1()、click2()
            TypeSpec listener = TypeSpec.anonymousClassBuilder("")
                    // 为当前Listener添加父类或者父接口, 这里使用的是匿名内部类的方式
                    .addSuperinterface(TypeUtil.ONCLICK_LISTENER)
                    .addMethod(MethodSpec.methodBuilder("onClick") // 定义方法名
                            .addAnnotation(Override.class) // 定义方法的继承关系
                            .addModifiers(Modifier.PUBLIC) // 定义方法的访问权限
                            .returns(TypeName.VOID) // 定义方法的返回值
                            .addParameter(TypeUtil.ANDROID_VIEW, "view") // 定义方法的参数
                            .addStatement("host.$N()", method.getMethodName()) // 定义方法内部的语句
                            .build())
                    .build();
            methodBuilder.addStatement("listener = $L ", listener);
            for (int id : method.ids) {
                methodBuilder.addStatement("finder.findView(source,$L).setOnClickListener(listener)", id);
            }
        }
        // 定义包名
        String packageName = getPackageName(mClassElement);
        // 定义类名
        String className = getClassName(mClassElement, packageName);
        ClassName bindClassName = ClassName.get(packageName, className);
        /*** 构建类*/
        TypeSpec finderClass = TypeSpec.classBuilder(bindClassName.simpleName() + "$$Injector")
                .addModifiers(Modifier.PUBLIC) // 定义类的访问权限
                // 为当前类添加父类或者父接口
                .addSuperinterface(ParameterizedTypeName.get(TypeUtil.INJECTOR, TypeName.get(mClassElement.asType())))
                .addMethod(methodBuilder.build())  // 将methodBuilder添加到类中
                .build();
        // 构建对应的java文件, 但是此时还没有生成对应的java文件
        return JavaFile.builder(packageName, finderClass).build();
    }

    public String getPackageName(TypeElement type) {
        return mElementUtils.getPackageOf(type).getQualifiedName().toString();
    }

    private static String getClassName(TypeElement type, String packageName) {
        int packageLen = packageName.length() + 1;
        return type.getQualifiedName().toString().substring(packageLen).replace('.', '$');
    }

    private Logger logger;

    public void setLogger(Logger logger) {
        this.logger = logger;
    }
}

六、Logger

public class Logger {
    private Messager messager;
    public Logger(Messager messager) {
        this.messager = messager;
    }
    public void info(String info) {
        if (messager == null) {
            return;
        }
        messager.printMessage(Diagnostic.Kind.NOTE, info);
    }
}
上一篇下一篇

猜你喜欢

热点阅读