ButterKnife源码分析

ButterKnife是一个专注于Android系统的View注入框架,之前只是简单的使用,知道其用注解的方式帮助减少我们findViewById(),同时在编译期自动生成XXXX_ViewBinding类帮组我们实现findViewById()和绑定回调方法的操作;但是具体的实现细节并不清楚,如何生成的XXXX_ViewBinding类的呢?以8.8.1版本为例;

本篇文章主要分为以下几个步骤

  • 注解相关知识;
  • 自动生成XXXX_ViewBinding类;
  • findViewById()等的具体操作;
注解

我们可以通过这面文章深入理解Java注解类型(@Annotation)了解关于注解的概念和自定义注解步骤;

编译生成XXXX_ViewBinding类

在编译期间,根据ButterKnifter的注解就会生成XXXX_ViewBinding类,是如何实现的呢?

processor是注解(Annotation)处理器的接口;其实现类必须有一个公共的无参构造函数,供工具用来初始化Processor;

1:如果Processor未使用现有对象,则通过构造函数初始化;

2: 调用init()方法;

3: 调用:getSupportedAnnotationTypes():返回支持注解类型的名称,getSupportedOptions和getSupportedSourceVersion()方法;

4: 调用process();

ButterKnifeProcessor类是AbstractProcessor的子类,其实现了processor接口;

主要看下process()方法通过javapoet类创建代码类(即XXXX_ViewBind.java文件);

public final class ButterKnifeProcessor extends AbstractProcessor {
    @Override 
    public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
    //获取使用ButterKnife注解的类;
    Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);

    //通过javapoet类创建类xxxx_ViewBinding类,
    for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
      TypeElement typeElement = entry.getKey();
      BindingSet binding = entry.getValue();

      JavaFile javaFile = binding.brewJava(sdk, debuggable, useLegacyTypes);
      javaFile.writeTo(filer);
    }

    return false;
  }
}
    

findAndParseTargets():找到使用ButterKnife注解的类,并一一解析;

private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
  Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
  Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();

  //以BindView为例;
  // Process each @BindView element.
  for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
      parseBindView(element, builderMap, erasedTargetNames);
  }

  Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
      new ArrayDeque<>(builderMap.entrySet());
    
  //返回bindingMap;主要是为BindingSet中的变量赋值为javapoet生成Java类确定方法、参数;
  Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
  while (!entries.isEmpty()) {
    Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();

    TypeElement type = entry.getKey();
    BindingSet.Builder builder = entry.getValue();

    TypeElement parentType = findParentType(type, erasedTargetNames);
    if (parentType == null) {
      bindingMap.put(type, builder.build());
    } else {
      BindingSet parentBinding = bindingMap.get(parentType);
      if (parentBinding != null) {
        builder.setParent(parentBinding);
        bindingMap.put(type, builder.build());
      } else {
        // Has a superclass binding but we haven't built it yet. Re-enqueue for later.
        entries.addLast(entry);
      }
    }
  }

  return bindingMap;
}

BindingSet类的brewJava();主要是根据不同的参数,通过javapoet创建出不同的java类;

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

createType():根据参数,创建不同的方法、变量等;具体可以参考javapoet的用法;

private TypeSpec createType(int sdk, boolean debuggable, boolean useLegacyTypes) {
  //创建类名,
  TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
      .addModifiers(PUBLIC);//public的
  //是否为final修饰
  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(useLegacyTypes));
  } else if (isActivity) {
    result.addMethod(createBindingConstructorForActivity(useLegacyTypes));
  } else if (isDialog) {
    result.addMethod(createBindingConstructorForDialog(useLegacyTypes));
  }
  if (!constructorNeedsView()) {
    // Add a delegating constructor with a target type + view signature for reflective use.
    result.addMethod(createBindingViewDelegateConstructor(useLegacyTypes));
  }
  result.addMethod(createBindingConstructor(sdk, debuggable, useLegacyTypes));

  //创建unbind()方法
  if (hasViewBindings() || parentBinding == null) {
    result.addMethod(createBindingUnbindMethod(result, useLegacyTypes));
  }

  return result.build();
}

编译期间就生成好了XXXX_ViewBinding类了,在app\build\generated\source\apt\debug\包名\ 目录下;

findViewById()等的具体操作

通过startActivity()初始化Activity之后,在onCreate()方法中调用ButterKnife.bind(this),在setContentView()方法之后;

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

createBinding():通过构造函数初始化对应的XXXX_ViewBinding类

private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
  Class<?> targetClass = target.getClass();
 //找到target对应的XXXX_ViewBinding的构造函数;
  Constructor<? extends Unbinder> constructor =    findBindingConstructorForClass(targetClass);

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

  //通过构造函数初始化XXXX_ViewBinding类;
  return constructor.newInstance(target, source);
}

findBindingConstructorForClass():找到对应的XXX_ViewBinding类构造函数;

@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
  //BINDINGS是LinkedHashMap,当前对象的cls作为key,对应的ViewBinding类的构造函数作为value;
  Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
  if (bindingCtor != null) {
    return bindingCtor;
  }
    
  String clsName = cls.getName();
  //系统的类除外
  if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
    return null;
  }

  Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
  //noinspection unchecked
  bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, 	      View.class);
  
  //存入LinkedHashMap中;
  BINDINGS.put(cls, bindingCtor);
  return bindingCtor;
}

通过构造函数初始化XXXX_ViewBinding类,以BottomNavigationActivity_ViewBinding为例

@UiThread
public BottomNavigationActivity_ViewBinding(BottomNavigationActivity target) {
  this(target, target.getWindow().getDecorView());
}

@UiThread
public BottomNavigationActivity_ViewBinding(final BottomNavigationActivity target, View source) {
  this.target = target;

  View view;
  //通过view.findViewById()找到对应的View;
  view = Utils.findRequiredView(source, R.id.message, "field 'mTextMessage' and method 'click'");
  //强转;
  target.mTextMessage = Utils.castView(view, R.id.message, "field 'mTextMessage'", TextView.class);
  view2131230856 = view;
  //通过@OnClick()绑定点击事件
  view.setOnClickListener(new DebouncingOnClickListener() {
    @Override
    public void doClick(View p0) {
      target.click();
    }
  });
  //通过@BindString()获取string;
  Context context = source.getContext();
  Resources res = context.getResources();
  target.name = res.getString(R.string.app_name);
}

以上就是ButterKnife的主要分析,如有问题,请多指教,谢谢!

参考文档

深入理解Java注解类型(@Annotation)

javapoet

javapoet-让你不再书写无聊的代码(一)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值