Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
197 lines (153 sloc) 6.39 KB
title tags date keywords
自定义注解,打造自己的框架(上篇)
Android
2019-11-23 15:09:29 -0800
自定义注解
Annotation
AutoService
AbstractProcessor

该系列介绍自定义注解,完成如下功能。

  • @BindView 代替 findViewById
  • @ClickResponder 代替 setOnClickListener
  • @LongClickResponder 代替 setOnLongClickListener
  • @IntentValue 代替 getIntent().getXXX
  • @UriValue 代替 getQueryParameter
  • @BroadcastResponder 代替 registerReceiver
  • @RouterModule、@RouterPath 来进行反依赖传递调用

使用编译时注解,生成辅助类来完成这些操作,尽量少的使用的反射功能。 该系列源码在https://github.com/huangyuanlove/AndroidAnnotation

本章先介绍一丢丢注解相关的东西,并且实现运行时注解

常见的注解:

@Override:被标记的方法一定是重写的父类方法,反之不一定;

@Deprecated:被标记的方法为过时方法,调用该方法时编辑器会有警告

@SuppressWarnings 指示编译器去忽略注解中声明的警告。

元注解 :作用在其他注解的注解

@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。值有三种类型(RetentionPolicy.SOURCE;RetentionPolicy.CLASS;RetentionPolicy.RUNTIME)

@Target - 标记这个注解应该是哪种 Java 成员。常见的值ElementType.TYPE(作用于类)、ElementType.FIELD(作用于字段)、ElementType.METHOD(作用于方法)等

更多关于注解的东西自己搜一下,搜不到的话不要往下看了

太长不看系列总结

  1. 声明注解
  2. 解析运行时注解 2.1 获取类的属性和方法 2.2 找到添加注解的属性或者方法 2.3 做自定义注解需要做的事情
  3. 解析编译时注解(需要编写注解处理器) 3.1 注册注解处理器(@AutoService) 3.2 拿到注解的属性和方法 3.3 生成辅助文件的内容(通常会使用javapoet) 3.4 写入文件 3.5 编写提供给用户调用的方法

注解的声明

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)//该注解可以标记字段
@Retention(RetentionPolicy.RUNTIME) //该注解可以在运行时通过反射拿到
/**
 * 用来代替findViewById
 */
public @interface BindView {
    int value() ; //这里可以声明默认值   int value() default -1;
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
/**
 * 用来代替setOnClickListener
 */
public @interface OnClick {
    int id();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
/**
 * 用来代替ssetOnLongClickListener
 */
public @interface OnLongClick {
    int id();
    boolean result() default true;
}

反射

java中的反射真是个毁誉参半的东西,至于怎么用,自己搜一下吧。使用反射调用非静态方法时,第一个参数是方法所在类的实例;调用静态方法时,第一个参数传null即可。

实现

新建Activity,随便写点布局、控件什么的。

使用方式和Butterknife差不多。

@BindView(value = R.id.test_runtime_annotation)
Button testRuntimeAnnotation;

@OnLongClick(id = R.id.test_runtime_annotation)
void testRuntimeAnnotationOnLongClick(View v) {
    Toast.makeText(this, "测试运行时注解:onLongClick", Toast.LENGTH_LONG).show();
}

@OnClick(id = R.id.test_runtime_annotation)
void setTestRuntimeAnnotationOnClick(View v) {
    Toast.makeText(this, "测试运行时注解:onClick", Toast.LENGTH_LONG).show();
}

我们需要在onCreate中对注解进行操作,

private void initAnnotation() {
    //反射获取所有声明的字段
    Field fields[] = this.getClass().getDeclaredFields();

    for (Field field : fields) {
        field.setAccessible(true);
        //判断字段是否有BindView注解
        BindView bindView = field.getAnnotation(BindView.class);
        if (bindView != null) {
            try {
                //对字段赋值
                field.set(this, findViewById(bindView.value()));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

	//反射获取所有方法
    Method methods[] = this.getClass().getDeclaredMethods();
    for (Method method : methods) {
        method.setAccessible(true);
        //判断方法是否有OnLongClick注解
        OnLongClick onLongClick = method.getAnnotation(OnLongClick.class);
        if (onLongClick != null) {
            //对该方法的注解值对应的控件 设置OnLongClickListener
            findViewById(onLongClick.id()).setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    try {
                        //方法调用
                        method.invoke(v.getContext(), v);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return onLongClick.result();

                }
            });
        }
         //判断方法是否有OnClick注解
        OnClick onClick = method.getAnnotation(OnClick.class);
        if (onClick != null) {
            //对该方法的注解值对应的控件 设置OnClickListener
            findViewById(onClick.id()).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        //方法调用
                        method.invoke(v.getContext(), v);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

上面就简单的实现了o'nClick,onLongClick,BindView注解,只不过使用的运行时注解,通过反射来对字段和注解进行操作。如果量大的话,反射会消耗性能,我们可以通过注解在编译期间生成辅助类来进行操作,比如 PermissionsDispatcher

下一篇介绍一下这种方式,需要用到 javapoet 这么个东西


以上

You can’t perform that action at this time.