8.5.3、阅读ButterKnife源码

大纲:0.0、android学习笔记_大纲

源码地址:https://github.com/ldy1993/ToolforAS.git

接上一节。我们看一下

自动生成的包如下

// Generated code from Butter Knife. Do not modify!
package com.example.function.study.D_手写框架.面向切面和依赖注入框架;

import android.view.View;
import android.widget.ListView;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import butterknife.Unbinder;
import butterknife.internal.Utils;
import com.ldy.study.R;
import java.lang.IllegalStateException;
import java.lang.Override;

public class ButterKnifeActivity_ViewBinding implements Unbinder {
  private ButterKnifeActivity target;

  @UiThread
  public ButterKnifeActivity_ViewBinding(ButterKnifeActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  public ButterKnifeActivity_ViewBinding(ButterKnifeActivity target, View source) {
    this.target = target;

    target.listView = Utils.findRequiredViewAsType(source, R.id.lv, "field 'listView'", ListView.class);
  }

  @Override
  @CallSuper
  public void unbind() {
    ButterKnifeActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    this.target = null;

    target.listView = null;
  }
}

因此,我们只需要在我们代码里面加一个注解。注解的compiler会自动帮我们将所要的依赖注入,生成新的实例甚至是运行的class类。

下面我们来研究和仿写这一个过程。

如果要阅读butterknife的作用流程,可以将:

annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'

改为

implementation 'com.jakewharton:butterknife-compiler:10.2.3'

我们就可以在lib中看到

 

我们可以简单分析一下源码,以注解一个BindView的控件为例

首先是我们应用代码里面

@BindView(R.id.lv)
ListView listView;

程序编译过程中会遍历这个类。获取到注解和注解的值

 @Override 
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
 //遍历注解,获得值放入bindingset对象,然后放入集合中  
 Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
//取出集合中bindingset对象,
    for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
      TypeElement typeElement = entry.getKey();
      BindingSet binding = entry.getValue();
//根据bindingset对象的数据进行源码的生成
      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;
  }

其中 findAndParseTargets中如下

  private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
....
//对其他注解的处理
.....
    for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
      // we don't SuperficialValidation.validateElement(element)
      // so that an unresolved View type can be generated by later processing rounds
      try {
        parseBindView(element, builderMap, erasedTargetNames);
      } catch (Exception e) {
        logParsingError(element, BindView.class, e);
      }
    }
.....
//将Map<TypeElement, BindingSet.Builder>和Set<TypeElement> erasedTargetNames转化成Map<TypeElement, BindingSet>
.....
    return bindingMap;
  }

其中 parseBindView中如下

private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
      Set<TypeElement> erasedTargetNames) {

    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
//下面是各种校验
/*isInaccessibleViaGeneratedCode 方法检验了:
方法修饰符不能为 private 和 static
包类型不能为非 Class
类的修饰符不能为 private
isBindingInWrongPackage 则检验了包名,不能以 android 或 java 开头。
*/
    // Start by verifying common generated code restrictions.
    boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
        || isBindingInWrongPackage(BindView.class, element);

    // Verify that the target type extends from View.
    TypeMirror elementType = element.asType();
    if (elementType.getKind() == TypeKind.TYPEVAR) {
      TypeVariable typeVariable = (TypeVariable) elementType;
      elementType = typeVariable.getUpperBound();
    }
    Name qualifiedName = enclosingElement.getQualifiedName();
    Name simpleName = element.getSimpleName();
    if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {
      if (elementType.getKind() == TypeKind.ERROR) {
        note(element, "@%s field with unresolved type (%s) "
                + "must elsewhere be generated as a View or interface. (%s.%s)",
            BindView.class.getSimpleName(), elementType, qualifiedName, simpleName);
      } else {
        error(element, "@%s fields must extend from View or be an interface. (%s.%s)",
            BindView.class.getSimpleName(), qualifiedName, simpleName);
        hasError = true;
      }
    }

    if (hasError) {
      return;
    }
//检验通过则调用 getOrCreateTargetClass 获取或生成一个builder,放入builderMap中。
//element放入erasedTargetNames中
    // Assemble information on the field.
    int id = element.getAnnotation(BindView.class).value();
    BindingSet.Builder builder = builderMap.get(enclosingElement);
    Id resourceId = elementToId(element, BindView.class, id);
    if (builder != null) {
      String existingBindingName = builder.findExistingBindingName(resourceId);
      if (existingBindingName != null) {
        error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",
            BindView.class.getSimpleName(), id, existingBindingName,
            enclosingElement.getQualifiedName(), element.getSimpleName());
        return;
      }
    } else {
      builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
    }

    String name = simpleName.toString();
    TypeName type = TypeName.get(elementType);
    boolean required = isFieldRequired(element);

    builder.addField(resourceId, new FieldViewBinding(name, type, required));

    // Add the type-erased version to the valid binding targets set.
    erasedTargetNames.add(enclosingElement);
  }

生成的源码如下

public class ButterKnifeActivity_ViewBinding implements Unbinder {
  private ButterKnifeActivity target;

  @UiThread
  public ButterKnifeActivity_ViewBinding(ButterKnifeActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  public ButterKnifeActivity_ViewBinding(ButterKnifeActivity target, View source) {
    this.target = target;

    target.listView = Utils.findRequiredViewAsType(source, R.id.lv, "field 'listView'", ListView.class);
  }

  @Override
  @CallSuper
  public void unbind() {
    ButterKnifeActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    this.target = null;

    target.listView = null;
  }
}

其中   target.listView = Utils.findRequiredViewAsType(source, R.id.lv, "field 'listView'", ListView.class);又去回调jar包中的方法。目的是对listView 进行创建实例赋值。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值