butterknife

首先从使用的地方来看:

@BindView(R.id.title) TextView title;
@BindView(R.id.subtitle) TextView subtitle;
@BindView(R.id.hello) Button hello;
@BindView(R.id.list_of_things) ListView listOfThings;
@BindView(R.id.footer) TextView footer;
@BindString(R.string.app_name) String butterKnife;
@BindString(R.string.field_method) String fieldMethod;
@BindString(R.string.by_jake_wharton) String byJakeWharton;
@BindString(R.string.say_hello) String sayHello;

上面是注解的,下面是在activity的oncreated方法使用的。注解的暂时先不管他,先看一看这个bind(this),方法做了什么!

  ButterKnife.bind(this);

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

可以看到这个sourceView其实就是activity的content布局的DecorView。
再跟踪到【createBinding】

findBindingConstructorForClass

butterknife_第1张图片
image.png

这里的这个bindingCtor就是两个参数的构造方法。
然后是

 Constructor constructor = findBindingConstructorForClass(targetClass);

if (constructor == null) {
  return Unbinder.EMPTY;
}

//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
  return constructor.newInstance(target, source);

这里就调用了两个参数的构造方法创建一个SimpleActivity_ViewBinding对象。

 @UiThread
public SimpleActivity_ViewBinding(final SimpleActivity target, View source) {
  this.target = target;

View view;
target.title = Utils.findRequiredViewAsType(source, R.id.title, "field 'title'", TextView.class);
target.subtitle = Utils.findRequiredViewAsType(source, R.id.subtitle, "field 'subtitle'", TextView.class);
view = Utils.findRequiredView(source, R.id.hello, "field 'hello', method 'sayHello', and method 'sayGetOffMe'");
target.hello = Utils.castView(view, R.id.hello, "field 'hello'", Button.class);
view2131099658 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
  @Override
  public void doClick(View p0) {
    target.sayHello();
  }
});
view.setOnLongClickListener(new View.OnLongClickListener() {
  @Override
  public boolean onLongClick(View p0) {
    return target.sayGetOffMe();
  }
});
view = Utils.findRequiredView(source, R.id.list_of_things, "field 'listOfThings' and method 'onItemClick'");
target.listOfThings = Utils.castView(view, R.id.list_of_things, "field 'listOfThings'", ListView.class);
view2131099666 = view;
((AdapterView) view).setOnItemClickListener(new AdapterView.OnItemClickListener() {
  @Override
  public void onItemClick(AdapterView p0, View p1, int p2, long p3) {
    target.onItemClick(p2);
  }
});
target.footer = Utils.findRequiredViewAsType(source, R.id.footer, "field 'footer'", TextView.class);
target.headerViews = Utils.listOf(
    Utils.findRequiredView(source, R.id.title, "field 'headerViews'"), 
    Utils.findRequiredView(source, R.id.subtitle, "field 'headerViews'"), 
    Utils.findRequiredView(source, R.id.hello, "field 'headerViews'"));

Context context = source.getContext();
Resources res = context.getResources();
target.butterKnife = res.getString(R.string.app_name);
target.fieldMethod = res.getString(R.string.field_method);
target.byJakeWharton = res.getString(R.string.by_jake_wharton);
target.sayHello = res.getString(R.string.say_hello);

}
这样就完成了绑定。
这个SimpleActivity_ViewBinding类是通过butterknife-compiler自动生成的。
这些代码是怎么生成的呢?
首先我们能用的注解是定义在

butterknife_第2张图片
image.png

对这些注解的处理主要是在compiler包下面的ButterKnifeProcessor。的process方法。

 @Override
 public boolean process(Set elements, RoundEnvironment env) {
  Map bindingMap = findAndParseTargets(env);

  for (Map.Entry 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;

}

这里面JavaFile是javapoet这个开源库里面的工具类,主要为了生成类,待会再看
在生成之前findAndParseTargets(env)查找和解析注解。

butterknife_第3张图片
image.png

其中的一段代码片段。其他的注解的处理类似都是走parseXXXXX。重点看下最常用的BindView吧。

 // Assemble information on the field.
int id = element.getAnnotation(BindView.class).value();

拿到注解传入的id参数。

builder = getOrCreateBindingBuilder(builderMap, enclosingElement);

处理完解析之后,是时候来看一看编译器自动生成java类的代码了。

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());
  }
}

brewJava方法

  JavaFile brewJava(int sdk, boolean debuggable) {
return JavaFile.builder(bindingClassName.packageName(), createType(sdk, debuggable))
    .addFileComment("Generated code from Butter Knife. Do not modify!")
    .build();
  }

createType方法

 private TypeSpec createType(int sdk, boolean debuggable) {
TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
    .addModifiers(PUBLIC);
if (isFinal) {
  result.addModifiers(FINAL);
}

if (parentBinding != null) {
  result.superclass(parentBinding.bindingClassName);
} else {
  result.addSuperinterface(UNBINDER);
}

if (hasTargetField()) {
  result.addField(targetTypeName, "target", PRIVATE);
}

if (isView) {
  result.addMethod(createBindingConstructorForView());
} else if (isActivity) {
  result.addMethod(createBindingConstructorForActivity());
} else if (isDialog) {
  result.addMethod(createBindingConstructorForDialog());
}
if (!constructorNeedsView()) {
  // Add a delegating constructor with a target type + view signature for reflective use.
  result.addMethod(createBindingViewDelegateConstructor());
}
result.addMethod(createBindingConstructor(sdk, debuggable));

if (hasViewBindings() || parentBinding == null) {
  result.addMethod(createBindingUnbindMethod(result));
}

return result.build();
}

createBindingConstructor方法

  private MethodSpec createBindingConstructor(int sdk, boolean debuggable) {
MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
    .addAnnotation(UI_THREAD)
    .addModifiers(PUBLIC);

if (hasMethodBindings()) {
  constructor.addParameter(targetTypeName, "target", FINAL);
} else {
  constructor.addParameter(targetTypeName, "target");
}

if (constructorNeedsView()) {
  constructor.addParameter(VIEW, "source");
} else {
  constructor.addParameter(CONTEXT, "context");
}

if (hasUnqualifiedResourceBindings()) {
  // Aapt can change IDs out from underneath us, just suppress since all will work at runtime.
  constructor.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class)
      .addMember("value", "$S", "ResourceType")
      .build());
}

if (hasOnTouchMethodBindings()) {
  constructor.addAnnotation(AnnotationSpec.builder(SUPPRESS_LINT)
      .addMember("value", "$S", "ClickableViewAccessibility")
      .build());
}

if (parentBinding != null) {
  if (parentBinding.constructorNeedsView()) {
    constructor.addStatement("super(target, source)");
  } else if (constructorNeedsView()) {
    constructor.addStatement("super(target, source.getContext())");
  } else {
    constructor.addStatement("super(target, context)");
  }
  constructor.addCode("\n");
}
if (hasTargetField()) {
  constructor.addStatement("this.target = target");
  constructor.addCode("\n");
}

if (hasViewBindings()) {
  if (hasViewLocal()) {
    // Local variable in which all views will be temporarily stored.
    constructor.addStatement("$T view", VIEW);
  }
  for (ViewBinding binding : viewBindings) {
    addViewBinding(constructor, binding, debuggable);
  }
  for (FieldCollectionViewBinding binding : collectionBindings) {
    constructor.addStatement("$L", binding.render(debuggable));
  }

  if (!resourceBindings.isEmpty()) {
    constructor.addCode("\n");
  }
}

if (!resourceBindings.isEmpty()) {
  if (constructorNeedsView()) {
    constructor.addStatement("$T context = source.getContext()", CONTEXT);
  }
  if (hasResourceBindingsNeedingResource(sdk)) {
    constructor.addStatement("$T res = context.getResources()", RESOURCES);
  }
  for (ResourceBinding binding : resourceBindings) {
    constructor.addStatement("$L", binding.render(sdk));
  }
}

return constructor.build();
  }

binding.render(sdk)对view绑定的关键在这里

 CodeBlock render(boolean debuggable) {
CodeBlock.Builder builder = CodeBlock.builder()
    .add("target.$L = $T.$L(", name, UTILS, kind.factoryName);
for (int i = 0; i < ids.size(); i++) {
  if (i > 0) {
    builder.add(", ");
  }
  builder.add("\n");

  Id id = ids.get(i);
  boolean requiresCast = requiresCast(type);
  if (!debuggable) {
    if (requiresCast) {
      builder.add("($T) ", type);
    }
    builder.add("source.findViewById($L)", id.code);
  } else if (!requiresCast && !required) {
    builder.add("source.findViewById($L)", id.code);
  } else {
    builder.add("$T.find", UTILS);
    builder.add(required ? "RequiredView" : "OptionalView");
    if (requiresCast) {
      builder.add("AsType");
    }
    builder.add("(source, $L, \"field '$L'\"", id.code, name);
    if (requiresCast) {
      TypeName rawType = type;
      if (rawType instanceof ParameterizedTypeName) {
        rawType = ((ParameterizedTypeName) rawType).rawType;
      }
      builder.add(", $T.class", rawType);
    }
    builder.add(")");
  }
}
return builder.add(")").build();
}

间接调用findviewbyid()

 public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);
if (view != null) {
  return view;
}

你可能感兴趣的:(butterknife)