Annotation注解在Android的开发中的使用越来越普遍,例如EventBus、ButterKnife、Dagger2等,记一次使用插件annotationProcessor实现下载监听注解框架,为什么不用android-apt呢,我不会告诉你android-apt不再维护了。
Javadoc代码注解-代码文档:传送门
注解简介
注解
Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
注解是一种元数据, 可以添加到java代码中. 类、方法、变量、参数、包都可以被注解,注解对注解的代码没有直接影响。之所以产生作用, 是对其解析后做了相应的处理. 注解仅仅只是个标记。
定义注解用的关键字是@interface
注解的用处
- 生成文档,编译进行检查。这是最常见的,即Javadoc代码注解
- 跟踪代码依赖性,实现替代配置文件功能。如EventBus、ButterKnife、Dagger2依赖注入等,本篇记录一次编译注解使用过程。
元注解
java.lang.annotation提供了四种元注解,专门注解其他的注解:
-
@Documented –注解是否将包含在Javadoc文档中
-
@Retention –什么时候使用该注解,有三种选择,默认为CLASS
- RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
- RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
- RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。
-
@Target –表示该注解用于什么地方。默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括
- ElementType.CONSTRUCTOR:用于描述构造器
- ElementType.FIELD:成员变量、对象、属性(包括enum实例)
- ElementType.LOCAL_VARIABLE:用于描述局部变量
- ElementType.METHOD:用于描述方法
- ElementType.PACKAGE:用于描述包
- ElementType.PARAMETER:用于描述参数
- ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明
-
@Inherited – 是否允许子类继承该注解,默认为false
Android中使用编译时注解,自动生成代码
这里只讲解编译注解,其他注解待续~~~
- DownloadAnnotations:Java工程,申明使用的注解
- DownloadCompiler:Java工程,用于编译期间自动生成代码
- DownloaderLibrary:下载依赖库
- Demo:测试示例
创建自定义注解
创建Java工程:DownloadAnnotations
-
声明一个Download注解,声明周期为Class,作用域为方法
@Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface Download { /** * 下载开始,获取文件大小 * @see com.excellence.downloader.FileDownloader.DownloadTask#getFileSize() * */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onPreExecute { String[] value() default { NO_URL }; } }
-
DownloadAnnotations的build.gradle配置
apply plugin: 'java' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) } tasks.withType(JavaCompile) { options.encoding = "UTF-8" } sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7
解析编译时注解
创建Java工程:DownloadCompiler
解析编译时注解需要继承AbstractProcessor,并且使用注解@AutoService(Processor.class)
,该注解在编译时自动执行被注解的类,即自动执行该类的Java程序,用于创建类文件。
-
DownloadProcessor事件注解扫描器
@AutoService(Processor.class) public class DownloadProcessor extends AbstractProcessor { private ElementHandler mElementHandler = null; /** * 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的{@link #init}方法,它会被注解处理工具调用,并输入{@link ProcessingEnvironment}参数。{@link ProcessingEnvironment}提供很多有用的工具类{@link Elements}、{@link Types}、{@link Filer}。 * * @param processingEnvironment */ @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); mElementHandler = new ElementHandler(processingEnvironment.getFiler(), processingEnvironment.getElementUtils()); } /** * 必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,在这里定义你的注解处理器注册到哪些注解上。 * * @return */ @Override public Set<String> getSupportedAnnotationTypes() { Set<String> annotations = new LinkedHashSet<>(); annotations.add(Download.onPreExecute.class.getCanonicalName()); return annotations; } /** * 用来指定你使用的Java版本。 * * @return */ @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } /** * 这相当于每个处理器的主函数main()。 在这里写扫描、评估和处理注解的代码,以及生成Java文件。输入参数{@link RoundEnvironment},可以让查询出包含特定注解的被注解元素。 * 该方法返回ture表示该注解已经被处理, 后续不会再有其他处理器处理; 返回false表示仍可被其他处理器处理 * * @param set * @param roundEnv * @return */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) { mElementHandler.clean(); mElementHandler.handleDownload(roundEnv); mElementHandler.createProxyFile(); return true; } }
-
ElementHandler元素处理(代码过长,请查看源码)
-
DownloadCompiler的build.gradle配置
com.google.auto.service:auto-service:1.0-rc2
谷歌提供的Java 生成源代码库com.squareup:javapoet:1.9.0
提供了各种 API 让你用各种姿势去生成 Java 代码文件,javapoet传送门apply plugin: 'java' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.auto.service:auto-service:1.0-rc2' compile 'com.squareup:javapoet:1.9.0' compile project(':DownloadAnnotations') } tasks.withType(JavaCompile) { options.encoding = "UTF-8" } sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7
到此注解生成代码过程结束
Downloader依赖库处理注解
-
根据DownloadCompile里的ElementHandler类创建注解接口ISchedulerListener
public interface ISchedulerListener<TASK> { void onPreExecute(TASK task); void onProgressChange(TASK task); void onProgressSpeedChange(TASK task); void onCancel(TASK task); void onError(TASK task); void onSuccess(TASK task); }
-
DownloadScheduler事件调度器,注册、解绑、监听,用于处理任务状态的调度
/** * 初始化代理参数,获取注解类集合 */ private void initDownloadCounter() { try { Class clazz = Class.forName("com.excellence.downloader.ProxyClassCounter"); Method download = clazz.getMethod("getDownloadCounter"); Object object = clazz.newInstance(); Object downloadCounter = download.invoke(object); if (downloadCounter != null) mDownloadCounter = unmodifiableSet((Set<String>) downloadCounter); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }
Demo使用注解监听
使用方式
// 注册
Downloader.register(this);
// 解绑
Downloader.unregister(this);
// 监听
@Download.onPreExecute
public void onPre(DownloadTask task)
{
/**
* 注解参数不添加URL,则获取全部任务的下载监听;
* 加了URL,则过滤出对应的任务的下载监听
* 如:@Download.onPreExecute({URL1, URL2})
*/
}