APT、android-apt 和 annotationProcessor 的区别
- APT是什么?
apt是 Annotation Processing Tool 的缩写,顾名思义,就是注解处理工具,用于编译时对注解进行解析,自动生成代码,并编译代码生成class文件,大体就是这个过程。 - android-apt是什么?
android-apt是第三方开源的注解处理框架,因为一开始Android没有默认的支持注解。dagger、ButterKnife等流行的注解框架,都是用的android-apt进行的注解处理。 - annotationProcessor是什么?
在Android studio Gradle插件2.2版本发布后,Google开始支持注解处理,而android-apt作者宣布不再维护更新,建议使用官方提供的annotationProcessor。 - android-apt 和 annotationProcessor异同
相同:两者都是apt 工具,只在编译的时候执行依赖的库,但是库最终不打包到apk中。
不同:
1)前者只支持 javac 的编译方式,而后者支持 javac 和 jack 两种。
2)Android Gradle 插件2.2版本后已经内置annotationProcessor,不需要引入,直接在build.gradle文件配置使用。
-
以dagger的使用为例,
1)添加android-apt框架的方法: -
step a)在project的build.gradle中添加
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //配置插件
}
}step b)在app的build.gradle中添加
apply plugin: 'com.neenbedankt.android-apt' //应用插件
dependencies {
compile 'com.squareup.dagger:dagger:1.1.0'
apt 'com.squareup.dagger:dagger-compiler:1.1.0' //注解编译器
}
2)添加annotationProcessor框架的方法step a)在project的build.gradle中添加
-
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0' // Gradle 为 2.2及以后的版本
}
}
step b)在app的build.gradle中添加
dependencies {
compile 'com.google.dagger:dagger:2.0'
annotationProcessor 'com.google.dagger:dagger-compiler:2.0'
}
使用annotationProcessor后,只需要配置这一处即可,但是不要忘记,必须要在Gradle plugin 2.2及以上才能使用。
注解的定义
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明 。注解不会改变编译器的编译方式,也不会改变虚拟机指令执行的顺序,它更可以理解为是一种特殊的注释,本身不会起到任何作用,需要工具方法或者编译器本身读取注解的内容继而控制进行某种操作。
注解的用途
注解可以在代码编译期间帮我们完成一些复杂的准备工作。
比如Greendao,我们注解一个实体类,它要处理成好多逻辑关系类,这些逻辑类让我们自己去书写的话那将是一个庞大的代码量, extends AbstractDao等这些类。
比如BufferKnife,我们用注解将控件的属性传递给它,它将生成一些功能类去处理这些值,***_ViewBinding等这些类型的类。运行时注解的使用,比如Retrofit的@GET或者@POST等等都是运行时注解,它的注解的处理必须跟Retrofit的对象有关联,所以必须定义成运行时的。所以注解已经成为一种趋势,比如BufferKnife,EventBus,Dagger,Greendao,Arouter,Retrofit等。
元注解(meta-Annotation)
元注解有四种(首字母D-I-R-T),作用就是负责注解其他注解:
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AsynLog {
}
@Target
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
ElementType.CONSTRUCTOR | 修饰构造器 |
ElementType.LOCAL_VARIABLE | 修饰局部变量 |
ElementType.ANNOTATION_TYPE | 修饰注解 |
ElementType.PACKAGE | 修饰包 |
ElementType.PARAMETER | 修饰参数 |
ElementType.METHOD | 修饰方法 |
ElementType.FIELD | 修饰成员变量 |
ElementType.TYPE | 修饰类、接口或枚举类型 |
@Retention
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值有以下三种:
RetentionPolicy.SOURCE | 只在源代码中保留,一般都是用来增加代码的理解性或者帮助代码检查之类的,比如我们的Override; |
RetentionPolicy.CLASS | 默认的选择,能把注解保留到编译后的字节码class文件中,仅仅到字节码文件中,运行时是无法得到的; |
RetentionPolicy.RUNTIME | 注解不仅 能保留到class字节码文件中,还能在运行通过反射获取到,这也是我们最常用的。 |
@Documented
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Inherited
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
这些类型和它们所支持的类在java.lang.annotation包中可以找到。
如何实现自定义注解?
@interface自定义注解自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
定义注解格式:
public @interface 注解名 {定义体}
注解参数的可支持数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组
实例
有了上面的基础知识后,下面通过实例分析注解框架原理。
1、首先编写一个普通布局文件activity_main.xml 包含一个button:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试"/>
</LinearLayout>
如果在没有使用注解框架前我们的使用会是下面这样:
public class MainActivity extends Activity implements OnClickListener{
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 找到按钮
bt = (Button) findViewById(R.id.btn);
// 设置点击事件
bt.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Toast.makeText(this, "测试", Toast.LENGTH_LONG).show();
}
}
在使用了注解的写法后变成了这样:
public class MainActivity extends Activity {
@BindView(value = R.id.btn, click = "clickview")
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Butterfly.bind(this);
}
public void clickview() {
Log.d("MainActivity : ", "click");
}
}
注解框架是帮我们完成了findViewById(int)和setOnClickListener(this)的过程。
代码省去了findViewById(int)和setOnClickListener(this)的这些操作,变得更简洁了。如上代码 @BindView(value = R.id.btn, click = "clickview") 中的R.id.btn就