ButterKnife 算是一款知名老牌 Android 开发框架了,通过注解绑定视图,避免了 findViewById() 的操作,广受好评!由于它是在编译时对注解进行解析完成相关代码的生成,所以在项目编译时会略耗时,但不会影响运行时的性能。接下来让我们从使用到原理一步步深入了解这把黄油刀的故事!转载
零、简述:较早期的注解库是如何工作的呢?
我们可以看到在使用时需要ButterKnife.bind(this),大家会认为利用反射获取到这个target的Activity,然后获取到这个Activity上面的所有注解,最终通过反射获取到findViewById。这种是之前比较早期的注解库的实现方式。这种方式有个较大的缺点,就是在Activity运行时如果大量使用反射,则会影响APP的运行效率,造成卡顿、产生临时对象造成频繁GC。
较早期的注解框架是通过RUNTIME运行时工作的。也就是我们注解的生命周期是RunTime,然后在运行时通过反射完成注入。这种方式的实现比较简单,但是致命的问题就是会影响APP的运行时效率。
那么ButterKnife是如何解决上述问题的呢?
ButterKnife是利用APT技术,也就是编译时解析技术。ButterKnife虽然使用的注解注入框架,但是它并不是运行时的注解,而是编译期利用注解生成代码,因此这对程序的运行期是没有任何副作用的;当然缺点就是在编译期会有一点点的时间成本。因此这里有三个知识点是必须清楚的:注解、反射、注解处理器。
具体可以参考,运行时注解和编译期注解的工作流程:
Android组件化系列之编译期注解手写注解、注解处理器、及注解工作流程
Android组件化系列之动态注解手写注解、注解处理器、及注解工作流程
ButterKnife的工作原理
1、编译的时候扫描注解,并做相应的处理,生成java代码,生成Java代码是调用javapoet库生成的;
2、调用ButterKnife.bind(this);方法的时候,将ID与对象的上下文绑定在一起。ButterKnife通过自身的注解处理器,扫描this这个类中的所有相关注解,根据注解生成相应的java代码,这些java代码就是我们之前需要手写的findViewById这些方法。
以下内容基于 butterknife:8.8.1
版本,主要包括如下几个方面的内容:
- 简单使用
- 原理分析
- 注解处理器
- JavaPoet
首先编写一个 ButterKnife 简单使用的例子,方便后续的分析。先在 app的 build.gradle 中加入如下配置,完成 ButterKnife 引入:
dependencies {
......
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}
接下来在 Activity 中使用,界面上一个TextView
一个Button
,很简单就不解释了:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_title)
TextView title;
@OnClick(R.id.bt_submit)
public void submit() {
title.setText("hello world");
}
private Unbinder unbinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
unbinder = ButterKnife.bind(this);
}
@Override
protected void onDestroy() {
unbinder.unbind();
super.onDestroy();
}
}
二、原理分析
点击下@BindView,可以看到ButterKnife是使用了编译期的注解
/**
* 定义了一个用在属性上的运行时注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
int value();
}
@Retention定义了该注解被保留的时间长短,有三种值可以选择 1、SOURCE,表示注解保留在源码层面,编译的后就会被擦除 2、CLASS,表示注解只保留到编译阶段 3、RUNTIME,表示注解保留到运行时,可以反射获取注解信息 @Target定义了注解用在什么地方 1、FIELD,属性 2、METHOD,方法函数 3、TYPE,类/接口 4、PARAMETER,参数 5、CONSTRUCTOR,构造方法
然后我们编译一下项目。直觉告诉我们应该从ButterKnife.bind(this)
开始分析,因为它像是 ButterKnife 和 Activity 建立绑定关系的过程,看具体的代码:
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
sourceView
代表当前界面的顶级父 View,是一个FrameLayout
,继续看createBinding()
方法:
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?