Android中自定义注解处理器

注解和注解生成器

如果没有处理注解的工具,那么注解也不会有太大的作用。对于不同的注解有不同的注解处理器。虽然注解处理器的编写千变万化,但是也有处理标准。
参考文献:https://blog.csdn.net/jdfkldjlkjdl/article/details/110937447?utm_term=java%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-2-110937447&spm=3001.4430
参考文献:https://blog.csdn.net/yamadeee/article/details/96479009

1.反射基本知识

类和类加载器:Java虚拟机是通过类加载器将类信息加载到Java虚拟机中的。

a.类信息中包含类中的所有信息,可以通过反射拿到类中所有的信息。
//待反射拿到的类
public class ReflectDemo {

    public String name;
    private int age;

    public ReflectDemo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private ReflectDemo() {
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return showString();
    }

    private String showString() {
        return "Demo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

反射操作:

public class Demo {

    public static void main(String[] args) {

      	//1.获取类中声明的构造函数,私有&公开
        Constructor<?>[] declaredConstructors = ReflectDemo.class.getDeclaredConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
            show("declaredConstructors", constructor);
        }

      	//2.获取类中公开构造函数
        Constructor<?>[] constructors = ReflectDemo.class.getConstructors();
        for (Constructor<?> constructor : constructors) {
            show("constructor", constructor);
        }

      	//3.获取类中所有声明的函数,私有&公开
        Field[] declaredFields = ReflectDemo.class.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            show("declaredField", declaredField);
        }

      	//4.获取类中所有的共有函数,包括继承的
        Field[] fields = ReflectDemo.class.getFields();
        for (Field field : fields) {
            show("field", field);
        }
				
        Method[] declaredMethods = ReflectDemo.class.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            show("declaredMethod", declaredMethod);
        }

        Method[] methods = ReflectDemo.class.getMethods();
        for (Method method : methods) {
            show("method", method);
        }
    }

    private static void show(String tag, Object object)  {
        System.out.println(String.format("Demo tag : %s output : %s", tag, object.toString()));
    }
}
b.调用类中的方法
//反射调用对象中的toString方法
public class Demo {

    public static void main(String[] args) {

        try {
            ReflectDemo reflectDemo = new ReflectDemo("JC", 23);
            
          	//1.通过反射调用对象的toString方法
          	Method toString = ReflectDemo.class.getMethod("toString");
            show("toString", toString);
            String ret = (String) toString.invoke(reflectDemo);
            show("ret", ret);

          	//2.通过反射调用对象的field
            Field name = ReflectDemo.class.getField("name");
            String nameField = (String) name.get(reflectDemo);
            show("nameField", nameField);
          
          	//3.通过反射调用构造函数
          	Constructor<ReflectDemo> constructor = ReflectDemo.class.getConstructor(String.class, int.class);
            ReflectDemo reflectDemoConstructed = constructor.newInstance("luogou", 24);
            show("constructor", reflectDemoConstructed.toString());
          
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void show(String tag, Object object)  {
        System.out.println(String.format("Demo tag : %s output : %s", tag, object.toString()));
    }
}
2.反射拿到注解信息

注解根据Retention属性,可表明是保留在source、class还是runtime三个阶段。

//注解定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstAnno {

    String name();
    int age();
    String time();
}

//注解的使用
@FirstAnno(name = "JC", age = 27,time = "2021-8-7")
public class FirstClass {
}

由于,注解信息只在源码阶段存在,所以查看编译的字节码如下:

//1.保留策略为源码,所以编译的字节码没有注解信息了
public class FirstClass {
    public FirstClass() {
    }
}

//2.保留策略为class类,字节码就会有注解信息。但是注意,在运行期(代码中反射)是无法获取到的!!!字节码文件如下:
@FirstAnno(
    name = "JC",
    age = 27,
    time = "2021-8-7"
)
public class FirstClass {
    public FirstClass() {
    }
}

//3.保留策略为runtime类,字节码会有注解信息,并且还是可以在运行期获取到。字节码为文件同保留策略为class时候。


public class Demo {

    public static void main(String[] args) {

        Annotation[] annotations = FirstClass.class.getAnnotations();
        for (Annotation annotation : annotations) {
            show("annotation", annotation);
        }

        Annotation[] declaredAnnotations = FirstClass.class.getDeclaredAnnotations();
        for (Annotation declaredAnnotation : declaredAnnotations) {
            show("declaredAnnotation", declaredAnnotation);
        }

        FirstAnno[] annotationsByTypes = FirstClass.class.getAnnotationsByType(FirstAnno.class);
        for (FirstAnno annotationsByType : annotationsByTypes) {
            show("annotationsByType", annotationsByType);
        }

        FirstAnno[] declaredAnnotationsByTypes = FirstClass.class.getDeclaredAnnotationsByType(FirstAnno.class);
        for (FirstAnno declaredAnnotationsByType : declaredAnnotationsByTypes) {
            show("declaredAnnotationsByType", declaredAnnotationsByType);
        }
    }

    private static void show(String tag, Object object)  {
        System.out.println(String.format("Demo tag : %s output : %s", tag, object.toString()));
    }
}
3.三种保留策略的补充说明
  • 如果保留策略为source,那么注解信息只会保存在.java源文件中;
  • 如果保留策略为class,那么注解信息只会保存在.java源文件和.class字节码文件中;
  • 如果保留策略为runtime,那么注解信息会保存在.java源文件、.class字节码文件和在类加载后。注意,类加载后保留了注解信息,那么就可以在代码中通过反射拿到相应的注解信息了!

通常有两种,一是编译时注解处理器,一是运行时注解处理器。上面“2.反射拿到注解信息”其实就是一种运行时注解处理器的实现了。而编译时注解处理器,在工程编译过程中对注解进行处理,也就是执行操作的期间只在编译期间内,这会在“6.Android Studio中的注解处理器”中进行举例。

4.获取泛型参数
@FirstAnno(name = "JC", age = 27,time = "2021-8-7")
public class FirstClass<T, R> {
    T name;
    R info;
}

public class Demo {

    public static void main(String[] args) {

        FirstClass<Integer, String> demo = new FirstClass<>();
        TypeVariable<? extends Class<? extends FirstClass>>[] typeParameters = demo.getClass().getTypeParameters();
        for (TypeVariable<? extends Class<? extends FirstClass>> typeParameter : typeParameters) {
            show("typeParameter", typeParameter);
        }
    }

    private static void show(String tag, Object object)  {
        System.out.println(String.format("Demo tag : %s output : %s", tag, object.toString()));
    }
}
5.注解处理器

如果没有处理注解的工具,那么注解也只会在source、class或者runtime的代码中增加部分信息,是不会有太大的作用。想要注解生效,就需要针对这些信息进行解析的。

自定义注解处理器,其实就是反射获取注解的属性,例如运行时注解的获取在类型2中已经展示了。其实,Java中有更好的方式,通过继承实现AbstractProcessor即可。

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.example.processors.DemoAnno")
public class CustomProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        Messager messager = processingEnv.getMessager();
        for (Element ele : roundEnvironment.getElementsAnnotatedWith(DemoAnno.class)) {
            messager.printMessage(Diagnostic.Kind.NOTE, "printMessage:" + ele.toString() + ", kind: " + ele.getKind());
            // 获取注解的元数据
            DemoAnno annotation = ele.getAnnotation(DemoAnno.class);
            String annoInfo = String.format("annotation class: %s, value: %d, name: %s", annotation.getClass(), annotation.value(), annotation.name());
            messager.printMessage(Diagnostic.Kind.NOTE, annoInfo);
        }
        return true;
    }

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

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(DemoAnno.class.getCanonicalName());
        return annotations;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}
6.Android Studio中的注解处理器

在Android Studio中使用自定义注解处理器的方法,是无法通过继承实现AbstractProcessor的,这个在Android app或者library模块中都是无法使用的,Android中应该是有更加好的方式实现注解处理器功能的(可能是apt依赖的使用,后续研究补充)。
所以,想要在Android Studio中使用JDK自带的AbstractProcessor方式,那么就只能够在Android中创建Java或者Kotlin原生库,然后在内部实现相应功能,再被上层Android的app或者library模块使用。注意:Java原生库也无法通过依赖,然后使用Android的app或者library模块。
所以,通过在Android Studio中“File-New-New Module-Java or Kotlin Library”,创建原生的Java库。
在这里插入图片描述
项目结构如下所示,annotation和processors模块都是原生Java模块,而app就是Android的模块。
在这里插入图片描述

  • annotation模块是一个Java库,定义了各种注解内容,以底层api工具包被外部依赖。内容如下:
java
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface DemoAnno {
    int value();
    String name();
}
  • processors模块也是一个Java库,定义了各种注解处理器,被外部以注解处理器方式依赖。内容见“5.注解处理器”。另外,gradle文件需要依赖annotation模块:
dependencies {

    implementation project(':annotation')

    // auto service
    implementation 'com.google.auto.service:auto-service:1.0-rc6'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
}
  • app模块,直接使用了processors模块,并以来annotation模块,其gradle文件如下:
dependencies {

	//依赖注解接口模块
    implementation project(path: ':annotation')
    //使用注解处理器
    annotationProcessor project(':processors')
}
//MainActivity.java
@DemoAnno(value = 123, name = "JC")
public class MainActivity extends AppCompatActivity {

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

//MyService.java
@DemoAnno(value = 666, name = "GoGoGO")
public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

那么编译项目的时候便会在“build”窗口中看到如下日志输出:

.......
> Task :app:compileDebugJavaWithJavac
The following annotation processors are not incremental: processors.jar (project :processors).
Make sure all annotation processors are incremental to improve your build speed.
注: printMessage:com.example.annotationdemo.MainActivity, kind: CLASS
注: annotation class: class com.sun.proxy.$Proxy197, value: 123, name: JC
注: printMessage:com.example.annotationdemo.MyService, kind: CLASS
注: annotation class: class com.sun.proxy.$Proxy197, value: 666, name: GoGoGO
.......
BUILD SUCCESSFUL in 4s
31 actionable tasks: 31 executed

Build Analyzer results available
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值