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

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

文章目录

一、编译时技术简介

二、ButterKnife 原理分析

二、ButterKnife 生成 Activity_ViewBinding 代码分析





一、编译时技术简介


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



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



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


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



程序的周期 :


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


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


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



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



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


① 编译时注解 ;


② 注解处理器 ;



举例说明 : 使用 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;
    @Override
    protected 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;
  @UiThread
  public MainActivity_ViewBinding(MainActivity target) {
    this(target, target.getWindow().getDecorView());
  }
  @UiThread
  public MainActivity_ViewBinding(MainActivity target, View source) {
    this.target = target;
    target.hello = Utils.findRequiredViewAsType(source, R.id.hello, "field 'hello'", TextView.class);
  }
  @Override
  @CallSuper
  public 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 方法 ,



         


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



         


获取 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 @UiThread
  public 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 @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 (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 @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;
    }
    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 unchecked
      bindingCtor = (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;
  }
}



目录
相关文章
|
28天前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
19 1
|
2月前
|
Android开发 数据安全/隐私保护 iOS开发
安卓与iOS系统的发展趋势与比较分析
【2月更文挑战第6天】 在移动互联网时代,安卓和iOS系统作为两大主流移动操作系统,各自呈现出不同的发展趋势。本文将从技术角度出发,对安卓和iOS系统的发展方向、特点及未来趋势进行比较分析,以期为读者提供更深入的了解和思考。
35 4
|
2月前
|
Android开发
安卓SO层开发 -- 编译指定平台的SO文件
安卓SO层开发 -- 编译指定平台的SO文件
31 0
|
3月前
|
监控 Android开发 C语言
深度解读Android崩溃日志案例分析2:tombstone日志
深度解读Android崩溃日志案例分析2:tombstone日志
83 0
|
3月前
|
数据安全/隐私保护 Android开发
2023安卓逆向 -- 某合伙apk登录加密分析
2023安卓逆向 -- 某合伙apk登录加密分析
26 0
|
3月前
|
存储 Java 开发工具
Android开发的技术与开发流程
Android开发的技术与开发流程
161 1
|
3月前
|
Shell Android开发
RK android13编译环境搭建与常用编译命令
RK android13编译环境搭建与常用编译命令
72 0
|
23小时前
|
存储 Java Android开发
Android系统 设置第三方应用为默认Launcher实现和原理分析
Android系统 设置第三方应用为默认Launcher实现和原理分析
4 0
|
2月前
|
网络协议 算法 Android开发
安卓逆向 -- 实战某峰窝APP(动态分析)
安卓逆向 -- 实战某峰窝APP(动态分析)
29 4
|
2月前
|
安全 搜索推荐 Android开发
Android 与 iOS 的比较分析
【2月更文挑战第5天】 Android 和 iOS 是目前市场上两种最流行的移动操作系统,它们都拥有自己的特点和优势。本文将会分别从操作系统设计、应用生态、安全性等方面对这两种操作系统进行比较和分析,希望能够帮助读者更好地选择适合自己的移动设备。