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 原理 — 注解生成器
要实现一个注解处理器需要继承AbstractProcessor,并重写它的4个方法,同时必须要有一个无参的构造方法,以便注解工具能够对它进行初始化。
- init,会被注解处理工具调用,参数ProcessingEnvironment提供了Elements,Types,Filer,Messager 等。
- getSupportedAnnotationTypes(),指定注解处理器要处理哪些注解,返回一个字符串集合,包含要处理注解的全名。
- getSupportedSourceVersion, 指定使用的Java版本,通常这里返回SourceVersion.latestSupported()
- process,相当于每个处理器的main函数,在这里可以做扫描、评估和处理注解代码的操作,以及生成Java文件。
四、ButterKnife 原理总结
综上所述,可以知道ButterKnife是如何在编译时进行注解解析了,其步骤如下:
- 定义注解
- 定义一个继承自AbstractProcessor的注解处理器,重写它4个方法中
- 使用AutoService注册自定义的注解处理器
- 实现process方法,在这里处理注解
- 处理所有注解,得到TypeElement和注解信息等信息
- 使用JavaPoet生成新的Java类