Android开发利器之注解

Android注解笔记


简介

关于注解的文章网上也很多了,文章也很不错,本文总结记录在开发注解时所踩得坑,希望对你在开发中有所帮助。

不太清楚注解是何物?以及如何开发使用注解可以参考一下链接:

秒懂,Java 注解 (Annotation)你可以这样学
Android中注解的使用
Android编译时注解


注解概念

注解,可以理解为一种标签!对代码贴上(标签)注解,能使我们快速认识代码,以及辅助代码完成某一特定的功能。


注解

@Retention(CLASS) @Target(FIELD)
public @interface BindView {
    
    
  /** View ID to which the field will be bound. */
  @IdRes int value();
}

作用对象@Target

它指明我们这个BindView注解可以用在哪些地方,@Target可以取以下值:

public enum ElementType {
    
    
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}

比如Android中的ButterKnife中@BindView为FILED,可以用在成员属性上


生存范围@Retention

它指明注解存活使用的生命范围,可以取以下值:

public enum RetentionPolicy {
    
    
    SOURCE,  源代码有效
    CLASS,	 编译时有效
    RUNTIME		运行时有效
}

如@Override,为SOURCE只是在源代码有效,起标识作用,因为方法重写是java的语言特性,而不是Override带来的功能,所以只是标识作用,重写方法时要不要他都无所谓


注解成员

注解内部没有方法,只有成员以及默认default初值,在我们使用注解的时候为这些成员赋值,最后在注解解析器时会用到这些值


注解原理

开发时,我们开发了注解,并完成了作用对象和生存范围,然后再把我们开发的注解,如BindView用到其他代码处,这个时候除了SOURCE类型的注解外,还不能正常工作,因为它还需要一个注解解析器

一般来说,注解解析器都是运用了反射原理,扫描或者获取java类中的注解,然后反射获取该注解修饰的成员、方法、参数等,然后把注解的赋值,通过反射为这些成员赋值,或者调用其他方法来完成相应的功能


编译时注解

编译时注解只在编译时有效,一般来说,编译时注解是为了生存一些中间辅助类,然后运行时用到。
首先,我们在开发在Module里面开发,然后在主工程app内引入
在这里插入图片描述
apt-annotation是我们开发的注解工程(如BindView.java),java-lib
apt-api是开发注解的注入工程(如ButterKnife.java),android-lib
apt-processor则是注解解析器工程,java-lib,这部分通过annotationProcessor引入主工程,源代码将不会封装到apk

  1. apt-processor需要引入的工程:
implementation project(':apt-annotation')
implementation 'com.squareup:javapoet:1.10.0'
implementation 'com.google.auto.service:auto-service:1.0-rc4'

apt-annotation是定义的注解工程,因为注解解析器需要知道我们要分析哪些注解
javapoet依赖主要是用于编译时执行
auto-service是为了让我们自动执行我们的解析器用的
但是这里我开发完成后并没有执行,最后还是我执行了我的注解解析器在哪里才执行了
在apt-processor工程下创建src/main/resources/META-INF/services/javax.annotation.processing.Processor,在这个文本文件中指明我的解析器,编译就自动生成了我的java文件

  1. 开发解析器
@AutoService(Processor.class)
//我需要解析哪些注解,可以是多个参数
@SupportedAnnotationTypes("com.my.BindeView")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class XAptProcessor extends AbstractProcessor{
    
    
	
	@Override 这个方法就是解析器的核心,执行逻辑再此
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    
    
		Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindeView.class);
        for (Element ele : elements) {
    
    
			1. 拿到每个Element的所属类
			2. 获取ele的注解以及注解里面的值
			3. 类名为key 注解以及ele为value存储到Map中
		}
		读取完所有注解后
		4. 依次取出每个类,以及其注解
		5. 为每个类创建方法MethodSpec
		6. 为每个类创建辅助类TypeSpec
		7. 创建java文件JavaFile
	}
}
  1. 编写注解注入类,在代码中调用此类,利用反射生成我们的辅助类
public class XcInject {
    
    
    public static void bind(Object host) {
    
    
        String name = host.getClass().getName();
        try {
    
    
            Class<?> aClass = Class.forName(name + "_ViewInjector");
            Method method = aClass.getMethod("inject", host.getClass(), Object.class);
            method.invoke(aClass.newInstance(),host,host);
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        } catch (InvocationTargetException e) {
    
    
            e.printStackTrace();
        } catch (InstantiationException e) {
    
    
            e.printStackTrace();
        }
    }
}

最后,我们在代码中调用XcInject.bind(this),就是在内存中生成我们的辅助类了,在用反射调用方法即可


运行时注解

运行时注解和上面类似,但是是在运行时JVM中执行,在运行时,通过获取类的class,然后获取注解以及修饰成员,反射进行赋值等


总结

注解是为了提高我们开发效率而产生的,要注意它的使用场景和需求,因为他很多地方都用到了反射原理,如果某些场景优先时间效率,尽量不要使用运行时注解,建议换成编译时注解,使用反射较少,或者用其他代码逻辑替换

猜你喜欢

转载自blog.csdn.net/jackzhouyu/article/details/106113370