Android 框架式编程之 ButterKnife

BufferKnife作为框架式编程的重要组成部分,能够简化findViewById和setOnClickListener,简化资源注入,通过编译时注解来动态实现依赖注入.

BufferKnife能够极大的精简View层面的代码量,其配置方式如下:

compile 'com.jakewharton:butterknife:(latest version)'
annotationProcessor 'com.jakewharton:butterknife-compiler:(latest version)'

一、ButterKnife 使用方法

在Activity中使用,只需要使用@BindView对要注入的View进行注解修饰,然后在Activity的onCreate执行:

 ButterKnife.bind(this); // 必须在设置好布局事件后绑定当前的Activity

这样通过注解生成器,在编译时进行动态注入,生成的代码为:

public void bind(ExampleActivity activity) {
  activity.subtitle = (android.widget.TextView) activity.findViewById(2130968578);
  activity.footer = (android.widget.TextView) activity.findViewById(2130968579);
  activity.title = (android.widget.TextView) activity.findViewById(2130968577);
}

需要注意的是,当我们在Fragment或者Activity中使用的时候,ButterKnife会在bind之后返回给了你一个Unbinder实例,我们需要做的事情就是在适当的生命周期内调用unbind方法。

public class FancyFragment extends Fragment {
  @BindView(R.id.button1) Button button1;
  @BindView(R.id.button2) Button button2;
  private Unbinder unbinder;

  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fancy_fragment, container, false);
    unbinder = ButterKnife.bind(this, view);
    return view;
  }

  @Override public void onDestroyView() {
    super.onDestroyView();
    unbinder.unbind();
  }
}

二、ButterKnife 原理 — 注解

注解(Annotation)在Java中已经是很普遍的使用了,它其实就是一种标记信息,然后程序在编译或者运行的时候可以读取这个标记信息,去执行特定的逻辑,比如@BindView(R.id.tv_text) TextView tvText,程序在编译时会读取到这个@BindView注解,解析出它的值R.id.tv_text,再根据它注解的这个tvText,就可以生成类似 tvText = (TextView)findViewById(R.id.tv_text); 的功能代码。

注解按生命周期可以分为:

  • RetentionPolicy.SOURCE(源码注解),只在源码中存在,在编译时会被丢弃,通常用于检查性的操作,如@Override。

  • RetentionPolicy.CLASS(编译时注解),在编译后的class文件中依然存在,通常用于编译时处理,如ButterKnife的@BindView。

  • RetentionPolicy.RUNTIME(运行时注解),不仅在编译后的class文件中存在,在被jvm虚拟机加载之后,仍然存在,通常用于运行时处理,如Retrofit的@Get。

同时注解按使用的对象可以分为:

  • ElementType.TYPE(类型注解),标记在接口、类、枚举上。
  • ElementType.FIELD(属性注解),标记在属性字段上。
  • ElementType.METHOD(方法注解),标记在方法上。
  • ElementType.PARAMETER(方法参数注解),标记在方法参数上。
  • ElementType.CONSTRUCTOR(构造方法注解),标记在构造方法上。
  • ElementType.LOCAL_VARIABLE(本地变量注解),标记在本地变量上。
  • ElementType.ANNOTATION_TYPE(注解的注解),标记在注解上。
  • ElementType.PACKAGE(包注解),标记在包上。
  • ElementType.TYPE_PARAMETER(类型参数注解,Java1.8加入),标记类型参数上。
  • ElementType.TYPE_USE(类型使用注解,Java1.8加入),标记在类的使用上。

三、ButterKnife 原理 — 注解生成器

注解只是一种标记信息,所以需要我们自己去处理注解,注解的处理有编译时注解处理和运行时注解处理。运行时注解,我们可以通过反射获取注解信息,进而进行相应处理。而编译时注解就需要使用注解处理器(Annotation Processor)进行处理。那什么是注解处理器?
注解处理器是javac的一个工具,它用来在编译时扫描和处理注解(Annotation)。你可以自定义注解,并注册到相应的注解处理器,由注解处理器来处理你的注解。一个注解的注解处理器,以Java代码(或者编译过的字节码)作为输入,生成文件(通常是.java文件)作为输出。这些生成的Java代码是在新生成的.java文件中,所以你不能修改已经存在的Java类,例如向已有的类中添加方法。这些生成的Java文件,会同其他普通的手动编写的Java源代码一样被javac编译。
要实现一个注解处理器需要继承AbstractProcessor,并重写它的4个方法,同时必须要有一个无参的构造方法,以便注解工具能够对它进行初始化。
下面是我们需要重写的四个方法:
  • init,会被注解处理工具调用,参数ProcessingEnvironment提供了Elements,Types,Filer,Messager 等。
  • getSupportedAnnotationTypes(),指定注解处理器要处理哪些注解,返回一个字符串集合,包含要处理注解的全名。
  • getSupportedSourceVersion, 指定使用的Java版本,通常这里返回SourceVersion.latestSupported()
  • process,相当于每个处理器的main函数,在这里可以做扫描、评估和处理注解代码的操作,以及生成Java文件。
然后添加AutoService注解库来实现注解处理器的注册,通过在你的注解处理器上加上@AutoService(Processor.class)注解,即可在编译时生成 META-INF/services/javax.annotation.processing.Processor 文件,并通过JavaPote生成新的Java类。

四、ButterKnife 原理总结

综上所述,可以知道ButterKnife是如何在编译时进行注解解析了,其步骤如下:

  • 定义注解
  • 定义一个继承自AbstractProcessor的注解处理器,重写它4个方法中
  • 使用AutoService注册自定义的注解处理器
  • 实现process方法,在这里处理注解
  • 处理所有注解,得到TypeElement和注解信息等信息
  • 使用JavaPoet生成新的Java类
这样,ButterKnife就通过动态生成的依赖注入代码,避免了之前版本中使用反射导致的性能降低。

 

 

posted @ 2017-05-24 17:27  灰色飘零  阅读(2040)  评论(0编辑  收藏  举报