Butter Knife的使用介绍与原理分析

Butter Knife 使用介绍

Butter Knife是基于Android的视图依赖注入框架,其原理是使用编译时注解处理生成相关辅助代码,在运行时进行辅助类的加载从而调用相关方法完成视图的注入。由于其是采用在源码编译时进行注解的处理,所以对应用的性能影响不大。使用它可以使你的代码更为整洁,优雅,同时在很大程度上加快你的编程速率,把你从繁琐的findViewById中解放出来。

下面我们简单的来看一个例子:
未使用Butter knife时,我们是通过findViewById()方法来查找view:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends AppCompatActivity {
TextView title;
TextView subtitle;
TextView footer;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

title = (TextView) findViewById(R.id.title);
subtitle = (TextView) findViewById(R.id.subtitle);
footer = (TextView) findViewById(R.id.footer);
}
}

使用Butter Knife我们可以在Activity中这样查找需要的view:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainActivity extends AppCompatActivity {
@Bind(R.id.title) TextView title;
@Bind(R.id.subtitle) TextView subtitle;
@Bind(R.id.footer) TextView footer;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ButterKnife.bind(this);
}
}

就如上面所看到的那样,在控件字段上使用@Bind注解并注明了资源id,Butter Knife就会自动地帮你注入需要的view。现在7.0.1版本已经由以前的@InjectView改为@Bind了,所以是不是应该叫做视图绑定更为合适呢?

值得注意的是,需要在setContentView方法之后加上

1
ButterKnife.bind(this);

这样Butter Knife才会工作。

Butter Knife 原理分析

Butter Knfie的工作原理一般分为两步:

  • 通过@Bind(R.id),@Onclick(R.id)等注解在编译的时候生成相关辅助代码,生成Java文件,编译器会将它们编译成对应的class文件
  • 通过ButterKnife.bind(this)等类似方法将ID与对应的上下文绑定在一起。

编译上面的代码,我们发现在classes文件下面生成了一个名为MainActivity$$ViewBinder.java的文件,也生成了对应的class文件MainActivity$$ViewBinder.class,生成的辅助类MainActivity$$ViewBinder.java源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Generated code from Butter Knife. Do not modify!
package com.upchina.www.butterknifeexampleapplication;

import android.view.View;
import butterknife.ButterKnife.Finder;
import butterknife.ButterKnife.ViewBinder;

public class MainActivity$$ViewBinder<T extends com.upchina.www.butterknifeexampleapplication.MainActivity> implements ViewBinder<T> {
@Override public void bind(final Finder finder, final T target, Object source) {
View view;
view = finder.findRequiredView(source, 2131492906, "field 'title'");
target.title = finder.castView(view, 2131492906, "field 'title'");
view = finder.findRequiredView(source, 2131492944, "field 'subtitle'");
target.subtitle = finder.castView(view, 2131492944, "field 'subtitle'");
view = finder.findRequiredView(source, 2131492945, "field 'footer'");
target.footer = finder.castView(view, 2131492945, "field 'footer'");
}

@Override public void unbind(T target) {
target.title = null;
target.subtitle = null;
target.footer = null;
}
}

那么Butter Knife是怎么工作的,并且怎么和编译时生成的辅助类进行加载与关联?
当我们在MainActivity调用ButterKnife.bind(this);方法时,调用的是ButterKnife里面的bind(Object target, Object source, Finder finder)这个方法,方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
static void bind(Object target, Object source, Finder finder) {
Class<?> targetClass = target.getClass();
try {
if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());
ViewBinder<Object> viewBinder = findViewBinderForClass(targetClass);
if (viewBinder != null) {
viewBinder.bind(finder, target, source);
}
} catch (Exception e) {
throw new RuntimeException("Unable to bind views for " + targetClass.getName(), e);
}
}

代码ViewBinder<Object> viewBinder = findViewBinderForClass(targetClass);得到的就是生成的辅助类MainActivity$$ViewBinder实例,代码viewBinder.bind(finder, target, source);就是调用辅助类实例的bind方法,如下:

1
2
3
4
5
6
7
8
9
@Override public void bind(final Finder finder, final T target, Object source) {
View view;
view = finder.findRequiredView(source, 2131492906, "field 'title'");
target.title = finder.castView(view, 2131492906, "field 'title'");
view = finder.findRequiredView(source, 2131492944, "field 'subtitle'");
target.subtitle = finder.castView(view, 2131492944, "field 'subtitle'");
view = finder.findRequiredView(source, 2131492945, "field 'footer'");
target.footer = finder.castView(view, 2131492945, "field 'footer'");
}

经过上面的分析,我初步理解了Butter Knife的工作原理,接下来会去研究下Java的注解以及Butter Knife的源码吧。