您的位置:首页 > 移动开发 > Android开发

Android开发学习之路--APT技术

2017-12-13 21:33 645 查看
今年都快要过去了,也已经2个月没有写博客了,主要还是换了新工作,今年都好几家徘徊了,从最初的公司散伙,也快1年了,这么背的17年终于快要结束了。不过庆幸的是加入了目前的公司,一个暂时觉得可以锻炼自己的平台。从嵌入式到app到嵌入式android系统,这次又回到了app,希望这次可以深耕3-5年,能在移动互联网站稳脚跟。两个月的时间忙于熟悉了解公司业务,也少了自己学习的时间,机器学习还没继续,android也没有深入了解,是时候补一把了。

以前遇到Dagger2, ButterKnife, EventBus3等的都是直接用,也没有太关心内部的源码实现。当想看的时候,发现一堆的注解不是非常好理解,所以还是先打打基础学习下apt技术吧,虽然不是那么新鲜了。

1.前言

首先看下butterknife生成的代码,要是都自己来敲,那就没法去和女神约会潇洒了。作为有家室的,也得多留点时间陪媳妇。

@BindView(R.id.iv_left_menu)
ImageView ivLeftMenu;
@BindView(R.id.iv_add)
ImageView ivAdd;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.rv_content)
RecyclerView rvRobots;
@BindView(R.id.coordinator_layout)
LinearLayout coordinatorLayout;


然后实际的生成的代码如下:

public class Main2Fragment_ViewBinding implements Unbinder {
private Main2Fragment target;

@UiThread
public Main2Fragment_ViewBinding(Main2Fragment target, View source) {
this.target = target;

target.ivLeftMenu = Utils.findRequiredViewAsType(source, R.id.iv_left_menu, "field 'ivLeftMenu'", ImageView.class);
target.ivAdd = Utils.findRequiredViewAsType(source, R.id.iv_add, "field 'ivAdd'", ImageView.class);
target.toolbar = Utils.findRequiredViewAsType(source, R.id.toolbar, "field 'toolbar'", Toolbar.class);
target.rvRobots = Utils.findRequiredViewAsType(source, R.id.rv_content, "field 'rvRobots'", RecyclerView.class);
target.coordinatorLayout = Utils.findRequiredViewAsType(source, R.id.coordinator_layout, "field 'coordinatorLayout'", LinearLayout.class);
}

@Override
@CallSuper
public void unbind() {
Main2Fragment target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;

target.ivLeftMenu = null;
target.ivAdd = null;
target.toolbar = null;
target.rvRobots = null;
target.coordinatorLayout = null;
}
}


2.注解

那么他是怎么实现的?在此之前需要了解下注解,可以参考下之前的一篇博客:

Android开发学习之路–Annotation注解简化view控件之初体验

一年前刚接触的时候写的,勉强还能理解哈,这里主要还是实现了运行时的注解,用了反射肯定是需要消耗一定的性能。上述的butterknife生成的代码可是编译时生成的代码,基本不消耗性能的。

3.什么是APT

理解了注解后,我们开始学习apt吧。

APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会将编译生成的源文件和原来的源文件一起生成class文件。

annotationProcessor:APT工具中的一种,他是google开发的内置框架,不需要引入,具体可以直接在build.gradle中使用,比如butterknife和dagger2引入如下:

dependencies {
compile 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

compile 'com.google.dagger:dagger:2.12'
annotationProcessor 'com.google.dagger:dagger-compiler:2.12'
}


4.APT注解的流程

1.定义注解(如@automain)

2.定义注解处理器

3.在处理器里面完成处理方式,通常是生成java代码。

4.注册处理器

5.利用APT完成如下图的工作内容。



5 APT的简单实现

既然了解了apt的过程,那么就来实现下了。

5.1 定义注解

新建一个java库,命名为annotation。

BindView注解:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}


Onclick注解:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface OnClick {
int[] value();
}


5.2 定义注解处理器

新建一个java库,命名为processor。

主要继承AbstractProcessor实现process方法生成对应的代码,至于AbstractProcessor何时被调用,怎么执行的,还没有做过多深入理解。利用了AutoService:

https://github.com/google/auto/tree/master/service。看一段官方的话:

AutoService will generate the file

META-INF/services/javax.annotation.processing.Processor in the output

classes folder.

在process方法里扫描所有的BindView和OnClick注解,然后javapoet来生成代码,这里生成代码的class为MAinActivity$$Finder.class,具体的实现可以参考github的例子,已经注释得很清楚了(例子源码在文末的链接中)。

若想要更深入理解javapoet的使用,可以参考这里:javapoet

5.3 编译生成代码

编译后在app/build/generated/source/apt/debug/com/jared/helloapt目录下会生成对应的MainActivity$$Finder.class文件

public class MainActivity$$Finder implements Finder<MainActivity> {
@Override
public void inject(final MainActivity host, Object source, Provider provider) {
host.tvInfo = (TextView)(provider.findView(source, 2131165309));
View.OnClickListener listener;
listener = new View.OnClickListener() {
@Override
public void onClick(View view) {
host.onClick(view);
}
};
provider.findView(source, 2131165219).setOnClickListener(listener);
provider.findView(source, 2131165220).setOnClickListener(listener);
}
}


很明显这里就是我们平时写的findview啊,setOnclickLIstener等方法的具体实现。通过注解,然后自动生成代码,注入到我们需要的类中。就免去了很多的重复劳动力,而且不会影响代码的运行效率。

5.4 注册处理器

新建一个android的library,命名为viewfinder。

实现ViewFinder的static的inject方法,用于注入代码。最后调用到注解处理器中生成的xxx$$Finder类的inject方法。其中这里xxx既是MainActivity。

6.总结下

这里我们再把整个过程理一遍。

编译期间

processor模块里的自定义的MyProcessor类的process会扫描所有的注解,然后生成自定义的XXX$$Finder.class代码。

使用期间

首先使用注解@BindView(R.id.tv_info)以及@OnClick({R.id.bt_change, R.id.bt_reset}),

接着MainActivity的onCreate方法中调用

ViewFinder.inject(this);方法来注入编译期间生成的代码。

关于apt技术基本上也了解了,说了那么多其实把例子敲一遍就都明白了。

github源码例子点击这里

参考:

http://blog.chengyunfeng.com/?p=1021

http://blog.csdn.net/u011315960/article/details/64441120

http://blog.csdn.net/hj7jay/article/details/52180023

https://github.com/sockeqwe/annotationprocessing101
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: