自定义注解总结

注解是个好东西!!!

刚接触的时候感觉很难又很神奇,在日常开发接触到的框架里面Eventbus,Aroute,Retrofit,ButterKnife等等一大堆便捷的工具,都会涉及到注解。用起来相当方便简洁,就能完成很多麻烦复杂且重复的工作,还能配合一些设计模式一起使用(工厂模式就很合适),会有很好的效果,但是自己却又不理解是怎么操作的,就去参考各路资料并进行一些简单小结,实现自定义注解。

注解是什么

注解是在编译和运行期间对代码的进行解析,配置,注释说明,等辅助性作用

笼统来说就是一个修饰符,标记某个地方,能够让系统根据这个注解找到对应的点,获取到一些信息,然后利用这些信息去完成我们的目的(比如检测,赋值什么的)。

注解的基本组成

1. @Retention    标明对应注解的生命周期

@Retention 对应类型值       注解生命周期

RetentionPolicy.SOURCE   编译完成class文件时,注解作用消失
RetentionPolicy.CLASS    编译完成后当没虚拟机加载的时候,注解消失
RetentionPolicy.RUNTIME   一直保存到运行时,注解作用一直保留

2. @Target    标明对应注解的生命周期

@Target 对应类型值             设置注解出现地点

ElementType.TYPE            接口、类、枚举、注解
ElementType.FIELD            字段、枚举的常量
ElementType.METHOD            方法
ElementType.PARAMETER        方法参数
ElementType.CONSTRUCTOR       构造函数
ElementType.LOCAL_VARIABLE      局部变量
ElementType.ANNOTATION_TYPE      注解
ElementType.PACKAGE            包

3. @Inherited  标明注解能否被继承(默认false)    
4. @Document     标明该注解能被工具文档化

这几个系统提供注解又称为元注解,用于说明注解的作用,即是注解的注解


自定义注解

自定义注解一般分为两种,一种是编译时的注解,另一种是运行时的注解,两者根据业务逻辑需要进行使用。两种自定义注解实现有区别,先简单说说对应作用

  • 自定义运行时注解

运行时的注解作用,代码运行时通过反射机制获取到对应的信息,然后进行一系列的操作,赋值,检测,绑定等等,需要注意设置要是运行时的注解,@Retention(RetentionPolicy.RUNTIME)

写一个类似Butterknife作用的绑定控件注解,先创建对应的注解(无聊顺便写上kottin的),当然Butterknife没那么简单,还有哈希表一类的缓存等等,需要考虑到反射性能问题。

@Target(value = {ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface BindViewJava {
    int value() default 0;
    int[] onClick() default 0;
}
@Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class BindViewKotlin(
        val id: Int = 0,
        val ids : IntArray = [0]
)

在编写对应注解的处理操作,根据反射获取到@BindViewJava的注解,在获取赋值在注解上的值value,进行控件view绑定,进行一系列操作,可以根据需要进行实现更多的便利操作,减少很多无用代码,本例子只是实现绑定View和View点击事件

public class AnnotationProcessor {

    public static void bind(final Activity object){
        try {
            final Class<? extends Activity> aClass = object.getClass();
            Field[] fields = aClass.getFields();//获取字段注解, public fields
            Method[] methods = aClass.getMethods();
            for (Field field : fields) {
                BindViewJava annotation = field.getAnnotation(BindViewJava.class);
                if (annotation != null && annotation.value() != 0){
                    View view = object.findViewById(annotation.value());
                    field.set(object, view); //将初始化的View返回给对应的class, 设置接收者为Activity
                }
            }

            for (final Method method : methods){
                BindViewJava annotation = method.getAnnotation(BindViewJava.class); //查找对应方法注解
                if (annotation != null && annotation.onClick() != null && annotation.onClick().length != 0){
                     final int[] ints = annotation.onClick();
                    for (int i = 0; i < ints.length; i++) {
                        object.findViewById(ints[i]).setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                try {
                                    method.invoke(object, v); //回调对应点击方法, 设置接收者为Activity
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                } catch (InvocationTargetException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                    }
                }
            }

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

}

相对来说比较简单粗糙,但是也能实现到我需要的控件绑定效果,搞清楚了运行时注解原理,就是运行期间通过反射获取带有对应类的信息,在进行处理。


最后上一下Activity,布局就是简单的TextVIew和两个按钮。

public class mainActivity extends AppCompatActivity {

    @BindViewJava(value = R.id.content)
    public TextView mTvContent;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AnnotationProcessor.bind(this);
        initView();
    }

    private void initView(){
        mTvContent.setText("View绑定");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @BindViewJava(onClick = {R.id.button_one, R.id.button_two})
    public void onClick(View view){
        switch (view.getId()){
            case R.id.button_one:
                Toast.makeText(this, "这是绑定按钮一", Toast.LENGTH_SHORT).show();
                break;
            case R.id.button_two:
                Toast.makeText(this, "这是绑定按钮二", Toast.LENGTH_SHORT).show();
                break;
        }
    }


}

一般对于项目来说如果在每个Activity都进行绑定的话,会相当麻烦毕竟有很多个,所以一般都考虑放在基类Activity里面进行绑定,可是实验了一下,如果直接移植到基类绑定,绑定点击事件会找不到ID资源,报错。



  • 自定义编译时注解

编译时的注解作用,编译期间扫描对应的注解,在自定义的注解处理器处理注解,生成对应的java文件进行使用,同样需要设置是编译期的注解@Retention(RetentionPolicy.CLASS),其他元注解根据业务需要编写

同样实现一个例子说明,开始说了可以搭配一些设计模式使用,搭配工厂模式实现实体类的生产,同样的目的也是为了简洁代码。

实现步骤

1. 自定义注解

  • 按照上面的方式定义一个自己的注解,周期为编译时期
/**
 * 生成类注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
@Inherited
public @interface ClassFactory {
    Class<?> className();
    Class<?> superClass();
}

2. 自定义注解处理器

@AutoService(ClassFactory.class)
public class AnimalProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(ClassFactory.class)) {

            // 检查被注解为@ClassFactory的元素是否是一个类
            if (annotatedElement.getKind() != ElementKind.CLASS) {
                return true; // 退出处理
            }

            //对注解的类进行处理
        }

        return false;
    }
}

3. 声明注解,绑定注解

@ClassFactory(className = duck.class, superClass = animal.class)
public class duck extends animal {

    @Override
    public String type() {
        return "鸭";
    }

    @Override
    public String run() {
        return "双脚直立行走";
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值