Annotation Processing 注解处理器简单使用

扔物线学习笔记啊

目标 小试牛刀:要实现类似ButterKnife 绑定视图

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.textView) TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InnerBinding.bind(this);

        textView.setText("卧龙凤雏");
    }
}

一 反射加注解

简单实现 1.同模块

public class InnerBinding {

    public static void bind(MainActivity activity) {
        activity.textView = activity.findViewById(R.id.textView);
    }
}

添加反射

public class InnerBinding {

    public static void bind(Activity activity) {
        //activity.textView = activity.findViewById(R.id.textView);
        //如果有多个控件 TextViw ImageView 需要反射
        // 1.遍历所有的Field
        for (Field field:activity.getClass().getDeclaredFields()){
            //2.如果Filed 是BindView注解(前面有BindView注解的作用就来了)
            BindView bindView = field.getAnnotation(BindView.class);
            //3.如果BindView非空,拿到值(例如R.id.textView)
            if (bindView!=null){
                try {
                    //4.赋值
                    field.set(activity,activity.findViewById(bindView.value()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

移动到lib-reflection模块,运行报错

因为此时TextView不是public,需要扩大反射权限

 //5.扩大访问权限
                    field.setAccessible(true);

完成通过反射bind

ButterKnife 不是依赖注入,是视图绑定

二 工具类加反射

通过在一个MainActivityBinding完成绑定操作,Binding只实例化MainActivityBinding

MainActivityBinding.java

public class MainActivityBinding {
    //初始化就设置
    public MainActivityBinding(MainActivity activity) {
        activity.textView = activity.findViewById(R.id.mTv);
    }
}

Binding.java

public class Binding {
    public static void bind(Activity activity){

        try {
            //获取类
            Class bindingClass = Class.forName(activity.getClass().getCanonicalName() + "Binding");
            //获取构造方法
            Constructor constructor = bindingClass.getDeclaredConstructor(activity.getClass());
            constructor.newInstance(activity);

        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}

MainActivity

public class MainActivity extends AppCompatActivity {
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Binding.bind(this);
        textView.setText("卧龙凤雏");
    }
}

三 反射加注解生成工具类

1.要通过注解自动生成 MainActivityBinding

  • 1.1创建一个 Module java or kotlin library 例如lib_processor
  • 1.2配置 固定的文件目录格式 resources =》META-INF=》services=》javax.annotation.processing.Processor 添加映射
  • 1.3app模块添加依赖 annotationProcessor
  • 1.4专门为注解创建一个模块 例如 lib_annoration 让后其他需要用的地方添加依赖
项目结构
  • 生成代码工具 Filer
  • javapoet 来生成的代码

BindingProcessor.java

/**
 * 注解处理的处理
 * 生成代码 processor
 * 在编译前执行
 */
public class BindingProcessor extends AbstractProcessor {
    Filer filer;

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

    /**
     * package com.example.annotation;
     *
     * public class MainActivityBinding {
     *     public MainActivityBinding(MainActivity activity) {
     *         activity.textView = activity.findViewById(R.id.mTv);
     *     }
     * }
     */

    /**
     * 进行具体的注解处理
     *
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {
        for (Element element : roundEnvironment.getRootElements()) {
            String packageStr = element.getEnclosingElement().toString();
            String classStr = element.getSimpleName().toString();
            ClassName className = ClassName.get(packageStr,classStr+"Binding");
            MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
                    .addModifiers(Modifier.PUBLIC)
                    .addParameter(ClassName.get(packageStr,classStr),"activity");
            boolean hasBinding = false;
            for (Element enclosedElement:element.getEnclosedElements()){
                if (enclosedElement.getKind() == ElementKind.FIELD) {
                    BindView bindView = enclosedElement.getAnnotation(BindView.class);
                    if (bindView!=null){
                        hasBinding = true;
                        constructorBuilder.addStatement("activity.$N = activity.findViewById($L)",
                                enclosedElement.getSimpleName(),bindView.value());
                    }
                }
            }
            TypeSpec builtClass = TypeSpec.classBuilder(className)
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(constructorBuilder.build())
                    .build();
            if (hasBinding){
                try {
                    JavaFile.builder(packageStr,builtClass)
                            .build().writeTo(filer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    /**
     * 进行注解处理的类型
     *
     * @return
     */
    @Override
    public Set getSupportedAnnotationTypes() {
        return Collections.singleton(BindView.class.getCanonicalName());
    }
}

BindView.java


@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView {
    int value();
}

你可能感兴趣的:(Annotation Processing 注解处理器简单使用)