傻瓜也可以看懂的——ButterKnife实现原理

       ButterKnife是一个专注于Android系统的View注入框架,以前总是要写很多findViewById来找到View对象,有了ButterKnife可以很轻松的省去这些步骤,目前使用很广。最重要的一点,使用ButterKnife对性能基本没有损失,因为ButterKnife用到的注解并不是在运行时反射的,而是在编译的时候生成新的class。项目集成起来也是特别方便,使用起来也是特别简单。

        今天,我们主要来源码分析一下ButterKnife的实现的原理:

拿到源码的第一步是从我们调用的地方来突破,那我们就来看看程序里面是怎样调用它的呢?在下面的代码,我们绑定ButterKnife。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    etUsername.setText("user name");
}

       以bind为分析的入口,从bind进入。

@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
  View sourceView = target.getWindow().getDecorView();
  return createBinding(target, sourceView);
}

        在点击creatBinding,在方法中我们发现,bind方法主要就是拿到我们绑定的Activity的Class,然后找到这个Class的ViewBinder,最后调用ViewBinder的bind()方法。

private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
  Class<?> targetClass = target.getClass(); // 拿到绑定activity的class
  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);
  }
}

       我们打开findViewBinderForClass()方法。

@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
  Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
  if (bindingCtor != null) {
    if (debug) Log.d(TAG, "HIT: Cached in binding map.");
    return bindingCtor;
  }
  String clsName = cls.getName();
  if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
    if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
    return null;
  }
  try {
    Class<?> bindingClass = Class.forName(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;
}

       重点是这行代码,我们根据这行代码反射自身。

 bindingCtor = findBindingConstructorForClass(cls.getSuperclass());

      这里用LinkhashMap做了下缓存,所以在6行的时候,就把刚刚反射的bindingCtor作为value,Class作为key加入这个LinkedHashMap,下次再bind这个类的时候,就直接在第3行的时候取出来用,提升性能。

发布了51 篇原创文章 · 获赞 97 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/m0_37218227/article/details/84103261