Version 10.1.0java
地址 github.com/JakeWharton…android
ButterKnife有两种实现方式git
源码下载编译遇到的一个问题github
直接git clone的Project的名称是butterknife,和里面的一个Module重名,致使gradle sync失败,须要clone时修改下名称。bash
Application Module和Library Module在使用上有点差异。app
android {
...
// Butterknife requires Java 8.
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'com.jakewharton:butterknife:10.1.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
}
复制代码
1.2 使用maven
不能给private或static添加,不然报错ide
class ExampleActivity extends Activity {
@BindView(R.id.user) EditText username;
@BindView(R.id.pass) EditText password;
@BindString(R.string.login_error) String loginErrorMessage;
@OnClick(R.id.submit) void submit() {
// TODO call server...
}
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
复制代码
project.gradle函数
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath 'com.jakewharton:butterknife-gradle-plugin:10.1.0'
}
}
复制代码
build.gradle源码分析
android {
...
// Butterknife requires Java 8.
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'com.jakewharton:butterknife:10.1.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
}
复制代码
添加plugin
apply plugin: 'com.jakewharton.butterknife'
复制代码
2.2 使用 须要用R2替换掉R,由于注解的值只支持常量,而Library Module中的R变量再也不是常量,ButterKnife生成的R2变量都是常量。
class ExampleActivity extends Activity {
@BindView(R2.id.user) EditText username;
@BindView(R2.id.pass) EditText password;
...
}
复制代码
(1) 从入口ButterKnife.bind()开始
/**
* BindView annotated fields and methods in the specified {@code target} using the {@code source}
* {@link View} as the view root.
*
* @param target Target class for view binding.
* @param source View root on which IDs will be looked up.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch {
......
}
}
复制代码
主要是经过绑定的类,获取继承Unbinder的类的构造方法,利用反射建立对象。
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
// 若是已经获取保存过,则直接返回
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
// 若是是系统类,则返回null
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
// 获取类名+"_ViewBinding"的类的构造函数
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch () {
......
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
复制代码
返回类名+"_ViewBinding"的类的构造函数。该类是如何产生的呢?
先别急,这个就是APT在编译时生成的。咱们先看下这个类的代码。在Project中build下工程。
public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);
if (view != null) {
return view;
}
......
}
复制代码
可见,View的获取,仍是经过Android提供的findViewById,和类型转换。
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.show(p0);
}
});
复制代码
点击事件,封装了一层DebouncingOnClickListener用来防快速点击。 这种防快速点击的方式咱们也能够直接应用到咱们的工程中。
Context context = source.getContext();
Resources res = context.getResources();
target.appname = res.getString(R.string.app_name);
复制代码
获取资源的方式,也是经过Resource。
(2) 下面分析下,如何在编译时生成类
关于APT技术和如何实现就不细说了,网上都有,很简单。
因此先从ButterKnifeProcessor proces方法开始讲起。
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env); // 第一行
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable);//第二行
try {
javaFile.writeTo(filer);//第三行
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
复制代码
具体生成代码的逻辑就不细说了,主要就是经过APT技术,遍历文件,查找自定义注解,封装成固定格式的javapoet的类,再生成文件。
(3) 说下ButterKnife如何在Module中生成R2文件,将资源定义成常量,编译在注解中使用,这个咱们开发中也能够借鉴。 juejin.im/post/5ce3aa…