1、基本介绍:【在程序编译阶段工作】
- 按照处理时期,注解分为两种类型,一种是运行时注解,另一种是编译时注解。
- 运行时注解:
- 运行时注解的实质是,在代码中通过注解进行标记,运行时通过反射寻找标记进行某种处理。而运行时注解一直以来被呕病的原因便是反射的低效。
- Retrofit运用了运行时注解。
- 编译时注解:
- 核心依赖APT(Annotation Processing Tools)实现,原理是在某些代码元素上(如类型、函数、字段等)添加注解,在编译时编译器会检查AbstractProcessor的子类,并且调用该类型的process函数,然后将添加了注解的所有元素都传递到process函数中,使得开发人员可以在编译器进行相应的处理,例如,根据注解生成新的Java类,这也就是EventBus,Retrofit,Dragger等开源库的基本原理。
- APT是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。 简单来说就是在编译期,通过注解生成.java文件。
- APT生成的源代码在
build/generated/source/apt
下可以看到。 - APT和代码插入是有本质区别的,APT是生成代码;代码插入是修改已有代码。
- APT注解处理器可以生成Java代码,这些生成的Java代码会组成 .java 文件,但不能修改已经存在的Java类(即不能向已有的类中添加方法)。而这些生成的Java文件,会同时与其他普通的手写Java源代码一起被javac编译。
2、APT优缺点:
2.1、难点:
- 在于设计模式和解耦思想的灵活应用。
- 在于代码生成的繁琐,你可以手动进行字符串拼接,当然有更高级的玩法用squareup的javapoet库,用建造者的模式构建出任何你想要的源代码。
2.2、优点:
- 它可以做任何你不想做的繁杂的工作,它可以帮你写任何你不想重复的模板代码。
- 它可以生成任何源代码供你在任何地方使用。
3、使用流程:
3.1、编写compiler java-library模块:(生成代理模板类)
3.1.1、编写业务Processor类:
- 继承AbstractProcessor抽象类,然后在process( )方法里做生成模板代码类的逻辑。
- 生成模板代码类的方法:可以手动进行字符串拼接书写,也可以用javapoet第三方库进行书写。
3.1.2、将编写好的业务Processor类注册到到javac中:
- 首先我们需要将我们的注解处理器打包到一个jar文件中。
- 其次在这个jar中,需要打包一个特定的文件
javax.annotation.processing.Processor
到META-INF/services
路径下。 - 打包进jar文件中的
javax.annotation.processing.Processor
的内容是:所有自定义注解处理器的合法全名列表,每一个自定义注解处理器全名用换行分割。 - javac会自动检查和读取
javax.annotation.processing.Processor
中的内容,并且注册自定义注解处理器作为注解处理器。 - 这一步可以用auto-service库简化处理逻辑:
- 在具体的业务注解处理器类上用注解
@AutoService(Processor.class)
修饰这个处理器类即可。
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
...
}
3.2、编写API模块去供用户访问生成的代理模板类:(实现一个ButterKnife的功能)
3.2.1、接口A:APT编译生成的所有业务代理类都会去实现这个接口。
public interface Finder<T> {
/**
*
* @param host 表示注解所在的业务类。
* {比如:MainActivity}
*
* @param source 表示调用具体业务模板的对象。
* {比如:实际调用findViewById的Activity对象、View对象}
*
* @param provider 是一个接口,定义了不同的{@code source}对象如何去调用具体的业务模板。
* {@linkplain ActivityProvider Activity如何去实例化控件}
* {@linkplain ViewProvider View如何去实例化控件}
*
*/
void inject(T host, Object source, Provider provider);
}
3.2.2、接口B:给接口A调用的,用于抽取具体的业务模块逻辑。比如抽取findViewById()实例化控件实例逻辑。
public interface Provider {
/**
* 获取{@code source}的上下文Context
* @param source 表示调用具体业务模板的对象。 {比如:实际调用findViewById的Activity对象、View对象}
* @return
*/
Context getContext(Object source);
/**
* 抽取出来的具体模板逻辑 {比如:findViewById()实例化控件逻辑}
* @param source 表示调用具体业务模板的对象。
* @param id 资源id
* @return
*/
View findView(Object source, int id);
}
3.2.3、接口B的实现类:
public class ActivityProvider implements Provider {
@Override
public Context getContext(Object source) {
return ((Activity) source);
}
@Override
public View findView(Object source, int id) {
return ((Activity) source).findViewById(id);
}
}
3.2.4、接口A的实现类:【自动生成的业务代理类】
public class MainActivity$$Finder implements Finder<MainActivity> {
@Override
public void inject(final MainActivity host, Object source, Provider provider) {
host.mTextView = (TextView)(provider.findView(source, 2131427414));
host.mButton = (Button)(provider.findView(source, 2131427413));
host.mEditText = (EditText)(provider.findView(source, 2131427412));
View.OnClickListener listener;
listener = new View.OnClickListener() {
@Override
public void onClick(View view) {
host.onButtonClick();
}
} ;
provider.findView(source, 2131427413).setOnClickListener(listener);
listener = new View.OnClickListener() {
@Override
public void onClick(View view) {
host.onTextClick();
}
} ;
provider.findView(source, 2131427414).setOnClickListener(listener);
}
}
3.2.5、API模块的入口类:
public class ViewRegister {
private static final ActivityProvider PROVIDER_ACTIVITY = new ActivityProvider();
private static final ViewProvider PROVIDER_VIEW = new ViewProvider();
/**
* key:宿主host类的类名
* value:自动生成的每个host类所对应的代理类实例
*/
private static final Map<String, Finder> FINDER_MAP = new HashMap<>();
/**
* Activity注入
* @param activity
*/
public static void inject(Activity activity) {
inject(activity, activity, PROVIDER_ACTIVITY);
}
/**
* View注入
* @param view
*/
public static void inject(View view) {
inject(view, view);
}
/**
* Fragment注入
* @param host
* @param view
*/
public static void inject(Object host, View view) {
inject(host, view, PROVIDER_VIEW);
}
/**
* 1、根据传入的host类(注解变量所在的类,比如MainActivity)去寻找我们生成的代理类。
* 2、将代理类强转为统一的接口类(这里所有代理类都实现了这个接口类)。
* 3、然后通过调用接口类提供的方法进而去调用具体代理类中的方法。
*
* @param host
* @param source
* @param provider
*/
public static void inject(Object host, Object source, Provider provider) {
String className = host.getClass().getName();
try {
Finder finder = FINDER_MAP.get(className);
if (finder == null) {
Class<?> finderClass = Class.forName(className + "$$Finder");
finder = (Finder) finderClass.newInstance();
FINDER_MAP.put(className, finder);
}
finder.inject(host, source, provider); //调用具体的代理类中的方法
} catch (Exception e) {
throw new RuntimeException("Unable to inject for " + className, e);
}
}
}
3.2.6、使用:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
TextView mTextView;
@BindView(R.id.btn)
Button mButton;
@BindView(R.id.et)
EditText mEditText;
@OnClick(R.id.btn)
public void onButtonClick() {
Toast.makeText(this, "onButtonClick", Toast.LENGTH_SHORT).show();
}
@OnClick(R.id.tv)
public void onTextClick() {
Toast.makeText(this, "onTextClick", Toast.LENGTH_SHORT).show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewRegister.inject(this);
}
}
了解更多,欢迎关注: