首先介绍下注解的基本知识,详细使用方法请自行百度
首先注解分为三类:
标准 Annotation,元 Annotation,自定义 Annotation
(1).标准 Annotation
包括 Override, Deprecated, SuppressWarnings,是java自带的几个注解,他们由编译器来识别,不会进行编译,
不影响代码运行,至于他们的含义不是这篇博客的重点,这里不再讲述。
(2).元 Annotation
@Retention, @Target, @Inherited, @Documented,它们是用来定义 Annotation 的 Annotation。也就是当我们要自定义注解时,需要使用它们。
(3).自定义 Annotation
根据需要,自定义的Annotation。而自定义的方式,下面我们会讲到。
同样,自定义的注解也分为三类,通过元Annotation - @Retention 定义:
@Retention(RetentionPolicy.SOURCE)
源码时注解,一般用来作为编译器标记。如Override, Deprecated, SuppressWarnings。
@Retention(RetentionPolicy.RUNTIME)
运行时注解,在运行时通过反射去识别的注解。
@Retention(RetentionPolicy.CLASS)
编译时注解,在编译时被识别并处理的注解
这里主要介绍下运行时注解,和编译时注解。
所谓运行时注解是指,注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;所以注解信息会加入到class文件中,在使用的时候通过反射方法拿到注解的传递数值.这就是为什么说,使用注解会影响性能,就是因为使用了反射。下面通过一个简单例子进行解释说明。
编写注解,含有名字name,年龄age ,年龄设置有默认值10
@Target({ElementType.TYPE,ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Deanitation {
String name();
int age() default 10;
}
@Retention(RetentionPolicy.RUNTIME)表示该注解是运行时注解,@Target({ElementType.TYPE,ElementType.FIELD, ElementType.METHOD})表示该注解能作用的范围,
在类中使用方法如下:
@Deanitation(name ="test")
public class Test {
@Deanitation(name = "run",age = 17)
public void run(){
System.out.print("run");
}
}
然后在Main方法执行过程中通过反射获取注解标注的信息:
public static void main(String[] args) {
try {
Class c=Class.forName("com.zhujie.Test");
//2.判断类上是否存在注解,并获取类上面注解的实例
if(c.isAnnotationPresent(Deanitation.class)){
Deanitation Description = (Deanitation) c.getAnnotation(Deanitation.class);
System.out.println(Description.name());
System.out.println(Description.age());
}
//3.判断方法上是否存在注解,并获取方法上面注解的实例
Method[] ms = c.getMethods();
for (Method method : ms) {
if(method.isAnnotationPresent(Deanitation.class)){
Deanitation Description = (Deanitation)method.getAnnotation(Deanitation.class);
System.out.println(Description.name());
System.out.println(Description.age());
}
}
//另一种获取方法上的注解的解析方法
for (Method method : ms) {
Annotation[] as = method.getAnnotations();
for (Annotation annotation : as) {
if(annotation instanceof Deanitation){
System.out.println(((Deanitation) annotation).name());
System.out.println(((Deanitation) annotation).age());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
以上就是运行时注解使用的基本方法,应为在使用过程中使用反射,所以对性能有影响,使用范围也有限制。下面模拟ButterKnife的使用过程,说明下编译时注解的使用。
建立项目后,因为要继承AbstractProcessor编写自定义注解解释器,所以要建立一个javalib的module,在改module下编写注解类DebindVeiw
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface DeBindView {
int value();
}
下面编写自定义注解解释器,主要是继承AbstractProcessor重写process方法,其中核心步骤是获取注解集合,然后生成所需要的java文件
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
messager.printMessage(Diagnostic.Kind.NOTE, "process...");
mProxyMap.clear();
Set<? extends Element> elesWithBind = roundEnv.getElementsAnnotatedWith(DeBindView.class);
for (Element element : elesWithBind) {
checkAnnotationValid(element, DeBindView.class);
VariableElement variableElement = (VariableElement) element;
//class type
TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
//full class name
String fqClassName = classElement.getQualifiedName().toString();
messager.printMessage(Diagnostic.Kind.NOTE, fqClassName);
ProxyInfo proxyInfo = mProxyMap.get(fqClassName);
if (proxyInfo == null) {
proxyInfo = new ProxyInfo(elementUtils, classElement);
mProxyMap.put(fqClassName, proxyInfo);
}
DeBindView bindAnnotation = variableElement.getAnnotation(DeBindView.class);
int id = bindAnnotation.value(); /**获取View iD**/
proxyInfo.injectVariables.put(id, variableElement);
}
/**
* 生成所需要的java类
*/
for (String key : mProxyMap.keySet()) {
ProxyInfo proxyInfo = mProxyMap.get(key);
try {
JavaFileObject jfo = processingEnv.getFiler().createSourceFile(
proxyInfo.getProxyClassFullName(),
proxyInfo.getTypeElement());
Writer writer = jfo.openWriter();
writer.write(proxyInfo.generateJavaCode());
writer.flush();
writer.close();
} catch (IOException e) {
error(proxyInfo.getTypeElement(),
"Unable to write injector for type %s: %s",
proxyInfo.getTypeElement(), e.getMessage());
}
}
return true;
}
下面生成的java文件
当然还要编写使用的API,我们要新建一个android lib module 在里面编写一个注册类,通过反射初始化 生成的类。达到初始化view findviewById()作用。
public class ViewInjector {
private static final String SUFFIX = "$$ViewInject";
public static void injectView(Activity activity) {
ViewInject proxyActivity = findProxyActivity(activity);
proxyActivity.inject(activity, activity);
}
public static void injectView(Object object, View view) {
ViewInject proxyActivity = findProxyActivity(object);
proxyActivity.inject(object, view);
}
private static ViewInject findProxyActivity(Object activity) {
try {
Class clazz = activity.getClass();
Class injectorClazz = Class.forName(clazz.getName() + SUFFIX);
return (ViewInject) injectorClazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
throw new RuntimeException(String.format("can not find %s , something when compiler.", activity.getClass().getSimpleName() + SUFFIX));
}
}
使用方法如下:
@DeBindView(R.id.tv_text)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_annotation);
ViewInjector.injectView(this);
}
以上就是简单的使用编译时注解,完成时间绑定,详细代码请下载查看,包含annotationapi ,javalib 两个module,其中annotationapi 依赖javalib 。使用的时候导入module 主module app只需要依赖annotationapi就可以了
下载地址:点击