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

Posted 东月之神

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发学习之路--APT技术相关的知识,希望对你有一定的参考价值。

今年都快要过去了,也已经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吧。

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

以上是关于Android开发学习之路--APT技术的主要内容,如果未能解决你的问题,请参考以下文章

Android开发学习之路--UI之自定义布局和控件

Android开发学习之路-回调实现Service向activity传递数据

Android开发学习之路-Flutter混合开发实践

Android开发学习之路-Flutter混合开发实践

android注解处理技术APT

Android开发学习之路--插件化基础动态代理Hook