从无到有手写ButterKnife框架

导航

一、代码的演进
二、butterKnife反射调用
三、javapoet自动生成模板代码
四、apt与注解
五、注解支持多层继承
六、apt调试
七、javapoet语法

1、前言

Annotation Processing Tool (apt)是编译期处理注解的工具

注解有三种【SOURCE,RUNTIME, CLASS】源码,运行时,编译,其中编译期效率最高的。

解决问题:通过注解,动态生成模板代码

apt与javapoet联动
1、抽象模板关键参数(只关注参数)
2、定义注解传入关键参数
3、引入apt
  • 1、抽象模板参数
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY CHENTONG.
 */
public class FirstActivity_ViewBinding implements Unbinder {
  private FirstActivity target;

  @UiThread
  public FirstActivity_ViewBinding(FirstActivity target, View source) {
    this.target = target;
    target.helloTv = source.findViewById( R.id.helloTv );
  }

  @Override
  public void unbind() {
    target.helloTv = null;
  }
}

这个类中核心的一句话
target.helloTv = source.findViewById( R.id.helloTv );
因为获得注解的同时,就会获得当前元素(Type,Field,Method),所以helloTv,这个field不用关注
关键参数就是元素ResId。

  • 2 定义注解
eg: @ViewId(R.id.helloTv)

1、新建module 选择选择java library ,工程名poet-annotation
2、新建注解 ViewId

@Retention(CLASS) @Target(FIELD)
public @interface ViewId {
    /** View ID to which the field will be bound. */
    @IdRes int value();
}
  • 3、apt
    新建module 命名为poet-compiler
    其中编写的时候,要注意包
    javax.annotation.processing
    javax.lang.model
    方法用法

base类 方便后续开发

public abstract class BaseProcessor extends AbstractProcessor {

    protected Filer mFiler; //输出位置
    protected Logger logger;
    protected Types types;
    protected Elements elementUtils;

    // Module name, maybe its 'app' or others
    protected String moduleName = null;
    protected Map options = null;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init( processingEnv );
        mFiler = processingEnv.getFiler();
        types = processingEnv.getTypeUtils();
        elementUtils = processingEnv.getElementUtils();
        logger = new Logger( processingEnv.getMessager() );
        options = processingEnv.getOptions();
        if (MapUtils.isNotEmpty( options )) {
            moduleName = options.get( KEY_MODULE_NAME );
        }
        if (StringUtils.isNotEmpty( moduleName )) {
            logger.info( "The user has configuration the module name, it was [" + moduleName + "]" );
        } else {
            logger.error( NO_MODULE_NAME_TIPS );
        }
    }

    //判断当前是不是activity类
    public boolean isActivity(TypeElement typeElement){
        TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
        if (types.isSubtype(typeElement.asType(),activityTm )) return true;
        return false;
    }

    //判断当前类是fragment
    public boolean isFragment(TypeElement typeElement){
        TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
        TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
        if (types.isSubtype(typeElement.asType(),fragmentTm )
                || types.isSubtype(typeElement.asType(),fragmentTmV4 )){
            return true;
        }
        return false;
    }

    @Override
    public Set getSupportedOptions() {
        return new HashSet() {{
            this.add( KEY_MODULE_NAME );
        }};
    }

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

注解生成代码类

@AutoService(Processor.class)
public class ViewIdProcessor extends BaseProcessor {

    private Map> parentAndChild = new HashMap<>();  //包含父类的注解

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init( processingEnv );
        logger.info(">>> ViewIdProcessor init. <<<");
    }

    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {

        if (CollectionUtils.isNotEmpty( annotations )){

            Set viewIdElements = roundEnv.getElementsAnnotatedWith(ViewId.class);
            try {
                categories( viewIdElements );

                //支持多层注解 field字段去重  不需要注释掉即可
                supportSuperAnnotation();

                gennerateHelper();
            } catch (Exception e) {
                logger.error( e );
            }

            return true;
        }
        return false;
    }

    //自动生成代码
    private void gennerateHelper() throws IOException {
        //根据类路径获得类型
        TypeElement type_unbinder = elementUtils.getTypeElement(Consts.UNBINDER);

        //用于判断当前类类型
        TypeMirror viewTm =  elementUtils.getTypeElement( Consts.VIEW ).asType();

        TypeElement uiThreadType =  elementUtils.getTypeElement( Consts.UI_THREAD );

        if (MapUtils.isNotEmpty( parentAndChild )){

            for (Map.Entry> entry : parentAndChild.entrySet()){
                TypeElement typeElement = entry.getKey();
                List elementList = entry.getValue();

                //类全路径
                String qualifiedName = typeElement.getQualifiedName().toString();
                //包名
                String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
                //类文件
                String fileName = typeElement.getSimpleName() + SUFFIX;

                //新建类Target_ViewBinding
                TypeSpec.Builder targetClassType = TypeSpec.classBuilder( fileName )
                        .addModifiers( Modifier.PUBLIC)
                        .addJavadoc( WARNING_TIPS )
                        .addSuperinterface( ClassName.get(type_unbinder.asType()) ); //实现接口

                //新建field target字段
                FieldSpec targetField = FieldSpec.builder(TypeName.get( typeElement.asType() ),"target",Modifier.PRIVATE)
                        .build();

                //target类增加一行field
                targetClassType.addField( targetField );


                //新建构造方法Target_ViewBinding(Target target,View source)
                MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
                        .addAnnotation( ClassName.get( uiThreadType ) )
                        .addModifiers( Modifier.PUBLIC )
                        .addParameter( TypeName.get( typeElement.asType() ),"target" )
                        .addParameter( TypeName.get( viewTm ),"source" )
                        .addStatement( "this.target = target" );

                for (Element element :elementList){

                    //获取控件ID
                    ViewId viewIdAnnotation = element.getAnnotation( ViewId.class );
                    int viewId = viewIdAnnotation.value();

                    //获取当前field字段
                    String fieldName = element.getSimpleName().toString();
                    constructorBuilder.addStatement( "target."+fieldName + " = source.findViewById( $L )" ,viewId);
                }

                //创建构造方法
                MethodSpec  constructor = constructorBuilder.build();

                //target类增加构造方法
                targetClassType.addMethod( constructor );

                //新建方法 unbind
                MethodSpec.Builder unbindBuilder = MethodSpec.methodBuilder("unbind")
                        .addAnnotation( Override.class )
                        .addModifiers( Modifier.PUBLIC )
                        .returns( void.class );

                for (Element element :elementList){
                    String fieldName = element.getSimpleName().toString();
                    unbindBuilder.addStatement( "target."+fieldName+" = null" );
                }
                //创建释放方法
                MethodSpec unbinder = unbindBuilder.build();

                //target类增加释放方法
                targetClassType.addMethod( unbinder );

                //创建target类
                TypeSpec targetType = targetClassType.build();

                //写类
                JavaFile.builder(packageName, targetType).build().writeTo(mFiler);
                //打印
                JavaFile.builder(packageName, targetType).build().writeTo(System.out);
            }

        }
    }

    /**
     *
     * @param elements
     * @throws IllegalAccessException
     *  当前类type元素和当前类元素的列表
     */
    private void categories(Set elements) throws IllegalAccessException {
        if (CollectionUtils.isNotEmpty(elements)) {
            for (Element element : elements) {
                //获得当前元素的TypeElement
                TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

                if (element.getModifiers().contains(Modifier.PRIVATE)) {
                    throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
                            + element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
                }

                if (parentAndChild.containsKey(enclosingElement)) { // Has categries
                    parentAndChild.get(enclosingElement).add(element);
                } else {
                    List childs = new ArrayList<>();
                    childs.add(element);
                    parentAndChild.put(enclosingElement, childs);
                }
            }

            logger.info("categories finished.");
        }
    }

    /**
     * Field元素 注解支持多层继承
     * 未做算法优化,仅做测试
     */
    private void supportSuperAnnotation(){
        TreeUtils tree = new TreeUtils( );
        parentAndChild = tree.supportSuperAnnotation( parentAndChild );
    }

    //建议这种写法,减少改字符串
    @Override
    public Set getSupportedAnnotationTypes() {
        return new HashSet() {{
            this.add( ViewId.class.getName() );
        }};
    }

}
从无到有手写butterKnife框架

https://github.com/yinlingchaoliu/JavaPoetDemo

具体参考
https://cloud.tencent.com/developer/article/1006210

你可能感兴趣的:(从无到有手写ButterKnife框架)