AndroidAndroid开发Android开发经验谈

08 ButterKnife源码解析

2021-08-31  本文已影响0人  凤邪摩羯

本文基于ButterKnife版本:

'com.jakewharton:butterknife:10.2.1'

'com.jakewharton:butterknife-compiler:10.2.1'

1-自定义注解处理器

java代码编译期,javac会调用java注解器来处理注解相关(属于编译期注解)。先看下butterknife-compiler库中的核心ButterKnifeProcessor

通过继承AbstractProcessor实现自定义处理器,重写以下方法

public class ButterKnifeProcessor extends AbstractProcessor {

    //指定java版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    //(1)初始化辅助类
    @Override
    public synchronized void init(ProcessingEnvironment env) {
        super.init(env);
        String sdk = env.getOptions().get(OPTION_SDK_INT);
        if (sdk != null) {
        try {
            this.sdk = Integer.parseInt(sdk);
        } catch (NumberFormatException e) {
            env.getMessager()
                .printMessage(Kind.WARNING, "Unable to parse supplied minSdk option '"
                    + sdk
                    + "'. Falling back to API 1 support.");
        }
        debuggable = !"false".equals(env.getOptions().get(OPTION_DEBUGGABLE));
        typeUtils = env.getTypeUtils();
        filer = env.getFiler();
        try {
            trees = Trees.instance(processingEnv);
        } catch (IllegalArgumentException ignored) {
            try {
                // Get original ProcessingEnvironment from Gradle-wrapped one or KAPT-wrapped one.
                for (Field field : processingEnv.getClass().getDeclaredFields()) {
                  if (field.getName().equals("delegate") || field.getName().equals("processingEnv")) {
                    field.setAccessible(true);
                    ProcessingEnvironment javacEnv = (ProcessingEnvironment) field.get(processingEnv);
                    trees = Trees.instance(javacEnv);
                    break;
                  }
                }
            } catch (Throwable ignored2) {
            }
        }
    }

    //返回目标注解集合
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
            types.add(annotation.getCanonicalName());
        }
        return types;
    }

    //(2)核心方法。先扫描查找注解,再生成java文件
    @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
    //@1.查找注解信息,生成BindingSet并保存到bingdingMap中
    Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
    //@4.遍历bindingMap,生成对应的 className_ViewBinding.java文件
    for (Map.Entry<TypeElement, BindingSet> 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;
  }
}

(1)初始相关辅助类init()

init方法中涉及的几个辅助类\变量

(2)核心操作process()方法
@1.扫描查找注解

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

    // @BindView 注解
    for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
      try {
        //@2.处理BindView的注解
        parseBindView(element, builderMap, erasedTargetNames);
      } catch (Exception e) {
        logParsingError(element, BindView.class, e);
      }
    }
    //省略@BindViews @BindString等注解处理
    。。。

    //@3.监听处理,OnClick,OnItemClick等
    for (Class<? extends Annotation> listener : LISTENERS) {
      findAndParseListener(env, listener, targetClassMap, erasedTargetNames);
    }

    for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {
      TypeElement parentType = findParentType(entry.getKey(), erasedTargetNames);
      if (parentType != null) {
        BindingClass bindingClass = entry.getValue();
        BindingClass parentBindingClass = targetClassMap.get(parentType);
        bindingClass.setParent(parentBindingClass);
      }
    }
}

@2.处理@BindView。这里涉及到BindingSet辅助类,缓存相关注解信息。ViewBiding

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

    // 检测注解语法是否合法。注解方法不能为private、static,注解类不能是android/java包
    // 若非法,抛出异常
    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;
    }

    // 获取注解中的对应的viewId
    int id = element.getAnnotation(BindView.class).value();
    BindingSet.Builder builder = builderMap.get(enclosingElement);
    Id resourceId = elementToId(element, BindView.class, id);
    if (builder != null) {
        //判断ID是否已经绑定过,如果已绑定则抛出异常
      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 {
        //未绑定过,则创建BindingSet.Builder
      builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
    }

    String name = simpleName.toString();
    TypeName type = TypeName.get(elementType);
    boolean required = isFieldRequired(element);
    //将当前注解生成FieldViewBinding并添加到builder中
    builder.addField(resourceId, new FieldViewBinding(name, type, required));

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

@3.监听处理,OnClick,OnItemClick等

private void findAndParseListener(RoundEnvironment env,
      Class<? extends Annotation> annotationClass,
      Map<TypeElement, BindingSet.Builder> builderMap, Set<TypeElement> erasedTargetNames) {
    for (Element element : env.getElementsAnnotatedWith(annotationClass)) {
      if (!SuperficialValidation.validateElement(element)) continue;
      try {
        //核心方法,解析注解
        //并将listener注解对应的信息封装到MethodViewBinding中
        //将MethodViewBinding添加到builder中
        parseListenerAnnotation(annotationClass, element, builderMap, erasedTargetNames);
      } catch (Exception e) {
        StringWriter stackTrace = new StringWriter();
        e.printStackTrace(new PrintWriter(stackTrace));

        error(element, "Unable to generate view binder for @%s.\n\n%s",
            annotationClass.getSimpleName(), stackTrace.toString());
      }
    }
  }

(3)注册自定义注解处理器。通过google提供的auto-service库来通过注解注册

compile 'com.google.auto.service:auto-service:1.0-rc2

@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
    ...
}

2-生成对应的className_ViewBinding.java

@4.遍历bindingMap,生成对应的 className_ViewBinding.java文件。例如业务使用

//ActivityB.java
public class ActivityB extends BaseActivity{
    @BindView(R.id.edit_txt)
    EditText editText;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_b);
        //@1.绑定
        ButterKnife.bind(this);
    }

    @OnClick(R.id.btn_one)
    protected void postMsg() {
        String msg = editText.getText().toString();
        EventBus.getDefault().post(MessageWrap.getInstance(msg));
        this.finish();
    }
}

经过安装编译后在指定目录下生成了ActivityB_ViewBinding.java。对应的方法都是指定在UI线程执行,且通过持有目标引用直接调用对应的属性或方法,所以要求注解的变量或方法必须为非private、static

public class ActivityB_ViewBinding implements Unbinder {
  private ActivityB target;//目标类的引用

  private View view7f070044;//与click事件绑定的view

  @UiThread
  public ActivityB_ViewBinding(ActivityB target) {
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  public ActivityB_ViewBinding(final ActivityB target, View source) {
    this.target = target;
    //实现findViewById及setOnClickListener
    View view;
    target.editText = Utils.findRequiredViewAsType(source, R.id.edit_txt, "field 'editText'", EditText.class);
    view = Utils.findRequiredView(source, R.id.btn_one, "method 'postMsg'");
    view7f070044 = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.postMsg();
      }
    });
  }
    //解绑
  @Override
  @CallSuper
  public void unbind() {
    ActivityB target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    this.target = null;

    target.editText = null;

    view7f070044.setOnClickListener(null);
    view7f070044 = null;
  }
}

使用原理,在ActivityB.setContentView之后调用ButterKnife.bind(this)来完成绑定。-->ButterKnife.bind(target, sourceView)。target即为ActivityB的实例,sourceView为其DecorView

@NonNull @UiThread
  public static Unbinder bind(@NonNull Object target, @NonNull View source) {
    //通过ActivityB的类名拼接获取到对应的ActivityB_ViewBinding.java类名
    Class<?> targetClass = target.getClass();
    if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
    //@5.获取对应ViewBinding类的构造器
    Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);

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

    //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
    try {
        //创建对应的ViewBinding类实例
        //即在ViewBinding初始化时完成了ActivityB的
        //findViewById及setOnClickListener等操作
      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);
    }
  }

@5.获取对应ViewBinding类的构造器

@Nullable @CheckResult @UiThread
  private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
    Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
    //从缓存中读取对应class的构造器
    if (bindingCtor != null || BINDINGS.containsKey(cls)) {
      if (debug) Log.d(TAG, "HIT: Cached in binding map.");
      return bindingCtor;
    }
    //java\android\androidx包下的类不处理
    String clsName = cls.getName();
    if (clsName.startsWith("android.") || clsName.startsWith("java.")
        || clsName.startsWith("androidx.")) {
      if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
      return null;
    }
    try {
        //获取ClassLoader并加载对应的ViewBinding类
      Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
      //通过反射获取ViewBinding的Class对象的Constructor即构造器
      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;
  }

3-流程总结

上一篇下一篇

猜你喜欢

热点阅读