失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 【Android APT】编译时技术 ( ButterKnife 原理分析 )

【Android APT】编译时技术 ( ButterKnife 原理分析 )

时间:2020-12-22 00:05:15

相关推荐

【Android APT】编译时技术 ( ButterKnife 原理分析 )

文章目录

一、编译时技术简介二、ButterKnife 原理分析二、ButterKnife 生成 Activity_ViewBinding 代码分析

一、编译时技术简介

APT ( Annotation Processing Tool ) 注解处理工具 ;

编译时技术 , 广泛应用在当前主流框架中 , 如 JetPack 中的 DataBinding , Room , Navigatoion , 第三方 ButterKnife , ARouter 等框架 ;

编译时技术 最重要的作用就是在编译时可以 生成模板代码 ;

由于生成代码操作是在编译时进行的 , 不会对运行时的性能产生影响 ;

程序的周期 :

源码期 :开发时 , 刚编写完 " .java " 代码 , 还未编译之前 , 就处于源码期 ;

编译期 :程序由 java 源码编译成 class 字节码文件 ;

运行期 :将字节码文件加载到 Java 虚拟机中运行 ;

编译时技术 APT 作用于 编译期 , 在这个过程中使用该技术 , 生成代码 ;

编译时技术 222 大核心要素 :在编译时 , 执行生成代码的逻辑 , 涉及到两个重要概念 ;

① 编译时注解 ;

② 注解处理器 ;

举例说明 : 使用 ButterKnife 时会依赖两个库 ,

dependencies {implementation 'com.jakewharton:butterknife:10.2.3'annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'}

其中com.jakewharton:butterknife:10.2.3是 编译时注解 ,com.jakewharton:butterknife-compiler:10.2.3是 注解处理器 ;

二、ButterKnife 原理分析

使用 ButterKnife :

① 添加依赖 :

dependencies {implementation 'com.jakewharton:butterknife:10.2.3'annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'}

② Activity 中使用 ButterKnife :

package kim.hsl.apt;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.widget.TextView;import butterknife.BindView;import butterknife.ButterKnife;public class MainActivity extends AppCompatActivity {@BindView(R.id.hello)TextView hello;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);hello.setText("ButterKnife");}}

BindView 注解分析 :在 TextView hello 成员变量处添加了 @BindView(R.id.hello) 注解 ;

@Target(FIELD) 元注解 : 表示其作用与类的成员字段 ;

@Retention(RUNTIME) 元注解 : 表示该注解保留到运行时阶段 ;

int value() 注解属性 : 只有一个注解属性 , 并且属性名是 value , 则使用注解时 “value =” 可省略 ;

@Retention(RUNTIME) @Target(FIELD)public @interface BindView {/** View ID to which the field will be bound. */@IdRes int value();}

TextView hello 需要使用findViewById进行赋值 , 在上述代码中没有写 findViewById 相关的代码 ; 肯定是在某个地方执行了 findViewById 的方法 ;

ButterKnife.bind(this)代码就是执行了 findViewById 方法 ;

ButterKnife 用到了编译时技术会 , 在项目编译时 , 会生成 MainActivity_ViewBinding 类 , 在该类中 , 会查找添加了 @BindView 直接的成员变量 , 再获取 注解属性 value 的值 , 然后调用 findViewById 方法获取组件并为成员变量赋值 ;

// Generated code from Butter Knife. Do not modify!package kim.hsl.apt;import android.view.View;import android.widget.TextView;import androidx.annotation.CallSuper;import androidx.annotation.UiThread;import butterknife.Unbinder;import butterknife.internal.Utils;import java.lang.IllegalStateException;import java.lang.Override;public class MainActivity_ViewBinding implements Unbinder {private MainActivity target;@UiThreadpublic MainActivity_ViewBinding(MainActivity target) {this(target, target.getWindow().getDecorView());}@UiThreadpublic MainActivity_ViewBinding(MainActivity target, View source) {this.target = target;target.hello = Utils.findRequiredViewAsType(source, R.id.hello, "field 'hello'", TextView.class);}@Override@CallSuperpublic void unbind() {MainActivity target = this.target;if (target == null) throw new IllegalStateException("Bindings already cleared.");this.target = null;target.hello = null;}}

二、ButterKnife 生成 Activity_ViewBinding 代码分析

调用 ButterKnife 静态方法Unbinder bind(@NonNull Activity target), 传入 Activity 对象 , 在方法中调用了 ButterKnife 的 bind 方法 ;

在 bind 方法中 , 先获取了 Activity 的类对象 ,

Class<?> targetClass = target.getClass();

然后将类对象传入了 findBindingConstructorForClass 方法 ,

Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);

在 findBindingConstructorForClass 方法中 , 获取了某个构造方法 ,

Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);

获取 Activity 类对象名称 , 即 " kim.hsl.apt.MainActivity " ,

String clsName = cls.getName();

得到名称后 , 判断该类对象是否是系统的 API , 如果是则退出 ; 如果不是 , 则继续向下执行 ,

if (clsName.startsWith("android.") || clsName.startsWith("java.")|| clsName.startsWith("androidx.")) {if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");return null;}

拼装要生成的类名称 , “kim.hsl.apt.MainActivity_ViewBinding” , 并自动生成该类 ;

Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");

ButterKnife 涉及到的源码 :

public final class ButterKnife {/*** BindView annotated fields and methods in the specified {@link Activity}. The current content* view is used as the view root.** @param target Target activity for view binding.*/@NonNull @UiThreadpublic static Unbinder bind(@NonNull Activity target) {View sourceView = target.getWindow().getDecorView();return bind(target, sourceView);}/*** 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 @UiThreadpublic 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 (IllegalAccessException e) {throw new RuntimeException("Unable to invoke " + constructor, e);} catch (InstantiationException e) {throw new RuntimeException("Unable to invoke " + constructor, e);} catch (InvocationTargetException e) {Throwable cause = e.getCause();if (cause instanceof RuntimeException) {throw (RuntimeException) cause;}if (cause instanceof Error) {throw (Error) cause;}throw new RuntimeException("Unable to create binding instance.", cause);}}@Nullable @CheckResult @UiThreadprivate 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;}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;}try {Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");//noinspection uncheckedbindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");} catch (ClassNotFoundException e) {if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());bindingCtor = findBindingConstructorForClass(cls.getSuperclass());} catch (NoSuchMethodException e) {throw new RuntimeException("Unable to find binding constructor for " + clsName, e);}BINDINGS.put(cls, bindingCtor);return bindingCtor;}}

如果觉得《【Android APT】编译时技术 ( ButterKnife 原理分析 )》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。