高效懒人工具ButterKnife原理解析

大家在使用butterknife的时候,是否注意到,需要调用类似这样的代码:ButterKnife.bind(this);

是否想到ButterKnife直接在这里对该类注解进行反射。那样ButterKnife就跟其他的ioc框架没有竞争力了。要知道大量的反射是严重影响性能的。

我们进入bind方法源码看看。
static void bind(Object target, Object source, Finder finder) {
  Class<?> targetClass = target.getClass();
  try {
    if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());
    ViewBinder<Object> viewBinder = findViewBinderForClass(targetClass);
    if (viewBinder != null) {
      viewBinder.bind(finder, target, source);
    }
  } catch (Exception e) {
    throw new RuntimeException("Unable to bind views for " + targetClass.getName(), e);
  }
}


从这个可以看出ButterKnife是调用了findViewBinderForClass方法获取到一个ViewBinder类,ViewBinder类是传进去的this的类名加上 "$$ViewBinder”。最后通过调用上面图的viewBinder.bind进行组件的绑定和监听。
private static ViewBinder<Object> findViewBinderForClass(Class<?> cls)
    throws IllegalAccessException, InstantiationException {
  ViewBinder<Object> viewBinder = BINDERS.get(cls);
  if (viewBinder != null) {
    if (debug) Log.d(TAG, "HIT: Cached in view binder map.");
    return viewBinder;
  }
  String clsName = cls.getName();
  if (clsName.startsWith(ANDROID_PREFIX) || clsName.startsWith(JAVA_PREFIX)) {
    if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
    return NOP_VIEW_BINDER;
  }
  try {
    Class<?> viewBindingClass = Class.forName(clsName + ButterKnifeProcessor.SUFFIX);
    //noinspection unchecked
    viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance();
    if (debug) Log.d(TAG, "HIT: Loaded view binder class.");
  } catch (ClassNotFoundException e) {
    if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
    viewBinder = findViewBinderForClass(cls.getSuperclass());
  }
  BINDERS.put(cls, viewBinder);
  return viewBinder;
}


到这里,就会想到ViewBinder是哪里来的呢,我们看编译后的源码文件会发现,咦,见鬼了,突然间多了一堆以后缀名$$ViewBinder源文件和class文件,点击该源文件查看,还真的有bind方法。
public class MainActivity$$ViewBinder<T extends com.xpg.menjiamatong_android.activity.MainActivity> implements ViewBinder<T> {
  @Override public void bind(final Finder finder, final T target, Object source) {
    View view;
    view = finder.findRequiredView(source, 2131492950, "method 'onClick'");
    view.setOnClickListener(
      new butterknife.internal.DebouncingOnClickListener() {
        @Override public void doClick(
          android.view.View p0
        ) {
          target.onClick(p0);
        }
      });
  }

  @Override public void unbind(T target) {
  }
}
其实这个java文件是在编译阶段生成的。那么,编译阶段是执行了哪个类呢,我们看下butterknife源码butterknife-7.0.1.jar!/META-INF/services/javax.annotation.processing.Processor文件中表明了哪个类是在编译阶段执行的,
butterknife.internal.ButterKnifeProcessor
这个类就是在编译阶段执行的。 ButterKnifeProcessor继承 AbstractProcessor。


ButterKnifeProcessor支持解析的注解有以下这几种:
@Override public Set<String> getSupportedAnnotationTypes() {
  Set<String> types = new LinkedHashSet<String>();

  types.add(Bind.class.getCanonicalName());

  for (Class<? extends Annotation> listener : LISTENERS) {
    types.add(listener.getCanonicalName());
  }

  types.add(BindBool.class.getCanonicalName());
  types.add(BindColor.class.getCanonicalName());
  types.add(BindDimen.class.getCanonicalName());
  types.add(BindDrawable.class.getCanonicalName());
  types.add(BindInt.class.getCanonicalName());
  types.add(BindString.class.getCanonicalName());

  return types;
}


包含这个注解类型的类都会被放在RoundEnvironment中,回调在process方法里。
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
  Map<TypeElement, BindingClass> targetClassMap = findAndParseTargets(env);

  for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {
    TypeElement typeElement = entry.getKey();
    BindingClass bindingClass = entry.getValue();

    try {
      JavaFileObject jfo = filer.createSourceFile(bindingClass.getFqcn(), typeElement);
      Writer writer = jfo.openWriter();
      writer.write(bindingClass.brewJava());
      writer.flush();
      writer.close();
    } catch (IOException e) {
      error(typeElement, "Unable to write view binder for type %s: %s", typeElement,
          e.getMessage());
    }
  }

  return true;
}


进而在findAndParseTargets中对注解类型进行解析,生成Map<TypeElement, BindingClass>。

private Map<TypeElement, BindingClass> findAndParseTargets(RoundEnvironment env) {
  Map<TypeElement, BindingClass> targetClassMap = new LinkedHashMap<TypeElement, BindingClass>();
  Set<String> erasedTargetNames = new LinkedHashSet<String>();

  // Process each @Bind element.
  for (Element element : env.getElementsAnnotatedWith(Bind.class)) {
    try {
      parseBind(element, targetClassMap, erasedTargetNames);
    } catch (Exception e) {
      logParsingError(element, Bind.class, e);
    }
  }

  // Process each annotation that corresponds to a listener.
  for (Class<? extends Annotation> listener : LISTENERS) {
    findAndParseListener(env, listener, targetClassMap, erasedTargetNames);
  }

  // Process each @BindBool element.
  for (Element element : env.getElementsAnnotatedWith(BindBool.class)) {
    try {
      parseResourceBool(element, targetClassMap, erasedTargetNames);
    } catch (Exception e) {
      logParsingError(element, BindBool.class, e);
    }
  }

  // Process each @BindColor element.
  for (Element element : env.getElementsAnnotatedWith(BindColor.class)) {
    try {
      parseResourceColor(element, targetClassMap, erasedTargetNames);
    } catch (Exception e) {
      logParsingError(element, BindColor.class, e);
    }
  }

  // Process each @BindDimen element.
  for (Element element : env.getElementsAnnotatedWith(BindDimen.class)) {
    try {
      parseResourceDimen(element, targetClassMap, erasedTargetNames);
    } catch (Exception e) {
      logParsingError(element, BindDimen.class, e);
    }
  }

  // Process each @BindDrawable element.
  for (Element element : env.getElementsAnnotatedWith(BindDrawable.class)) {
    try {
      parseResourceDrawable(element, targetClassMap, erasedTargetNames);
    } catch (Exception e) {
      logParsingError(element, BindDrawable.class, e);
    }
  }

  // Process each @BindInt element.
  for (Element element : env.getElementsAnnotatedWith(BindInt.class)) {
    try {
      parseResourceInt(element, targetClassMap, erasedTargetNames);
    } catch (Exception e) {
      logParsingError(element, BindInt.class, e);
    }
  }

  // Process each @BindString element.
  for (Element element : env.getElementsAnnotatedWith(BindString.class)) {
    try {
      parseResourceString(element, targetClassMap, erasedTargetNames);
    } catch (Exception e) {
      logParsingError(element, BindString.class, e);
    }
  }

  // Try to find a parent binder for each.
  for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {
    String parentClassFqcn = findParentFqcn(entry.getKey(), erasedTargetNames);
    if (parentClassFqcn != null) {
      entry.getValue().setParentViewBinder(parentClassFqcn + SUFFIX);
    }
  }

  return targetClassMap;
}

最后,解析Map<TypeElement, BindingClass>,生成前面提到的viewHolder源文件
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值