ButterKnife源码拆轮子学习

栏目: Android · 发布时间: 4年前

内容简介:Version 10.1.0地址ButterKnife有两种实现方式

Version 10.1.0

地址 github.com/JakeWharton…

ButterKnife有两种实现方式

  1. 定义注解,在运行时利用反射实现。
  2. 定义注解,在编译时利用APT生成固定格式的源文件。

源码下载编译遇到的一个问题

直接git clone的Project的名称是butterknife,和里面的一个Module重名,导致gradle sync失败,需要clone时修改下名称。

使用

Application Module和Library Module在使用上有点差别。

  1. Application Module 1.1 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'
}
复制代码

1.2 使用

不能给private或static添加,否则报错

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...
  }
}
复制代码
  1. Library Module 2.1 添加依赖

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;
  ...
}
复制代码

源码分析 先看下源码包结构

ButterKnife源码拆轮子学习

可见 支持反射实现和APT实现

  1. 首先分析APT的实现方式

(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下工程。

ButterKnife源码拆轮子学习
ButterKnife源码拆轮子学习
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的类,再生成文件。

  • 第一行,遍历所有自定义Bind注解,存到Map中
  • 第二行,生成javapoet中的JavaFile对象
  • 第三行,利用JavaFile对象生成具体文件

(3) 说下ButterKnife如何在Module中生成R2文件,将资源定义成常量,编译在注解中使用,这个我们开发中也可以借鉴。 juejin.im/post/5ce3aa…


以上所述就是小编给大家介绍的《ButterKnife源码拆轮子学习》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithms in Java

Data Structures and Algorithms in Java

Robert Lafore / Sams / 2002-11-06 / USD 64.99

Data Structures and Algorithms in Java, Second Edition is designed to be easy to read and understand although the topic itself is complicated. Algorithms are the procedures that software programs use......一起来看看 《Data Structures and Algorithms in Java》 这本书的介绍吧!

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具