Butterknife源码分析

96
34sir
2019.01.29 10:14 字数 71

目标

  • 实现原理
  • 设计模式

分析

核心模块.png
注解处理流程.png

基于上面已知信息 接下来分析源码:

ButterKnifeProcessor#init

   // TODO: 18-6-4 获取辅助类 
    @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));

        // TODO: 18-6-25  typeUtils 用来处理TypeMirror的工具类
        typeUtils = env.getTypeUtils();
        // TODO: 18-6-25 filer 用来生成java的源文件
        filer = env.getFiler();
        try {
            // TODO: 18-6-25 语法分析树
            trees = Trees.instance(processingEnv);
        } catch (IllegalArgumentException ignored) {
        }
    }

支持的注解:

@Override public Set<String> getSupportedAnnotationTypes() {
    Set<String> types = new LinkedHashSet<>();
    for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
      types.add(annotation.getCanonicalName());
    }
    return types;
  }
private Set<Class<? extends Annotation>> getSupportedAnnotations() {
    Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();

    annotations.add(BindAnim.class);
    annotations.add(BindArray.class);
    annotations.add(BindBitmap.class);
    annotations.add(BindBool.class);
    annotations.add(BindColor.class);
    annotations.add(BindDimen.class);
    annotations.add(BindDrawable.class);
    annotations.add(BindFloat.class);
    annotations.add(BindFont.class);
    annotations.add(BindInt.class);
    annotations.add(BindString.class);
    annotations.add(BindView.class);
    annotations.add(BindViews.class);
    annotations.addAll(LISTENERS);

    return annotations;
  }

 private static final List<Class<? extends Annotation>> LISTENERS = Arrays.asList(//
      OnCheckedChanged.class, 
      OnClick.class, 
      OnEditorAction.class, 
      OnFocusChange.class, 
      OnItemClick.class, 
      OnItemLongClick.class, 
      OnItemSelected.class, 
      OnLongClick.class, 
      OnPageChange.class, 
      OnTextChanged.class, 
      OnTouch.class
  );

ButterKnifeProcessor#process:核心方法

// 关键方法
    @Override
    public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
        // 查找所有的注解 并且根据注解生成BindingSet
        Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);

        // 遍历步骤map 生成.java文件也就是类名_ViewBinding的java文件
        for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
            TypeElement typeElement = entry.getKey();
            BindingSet binding = entry.getValue();
            // 使用开源库javapoet生成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());
            }
        }

        return false;
    }
  // TODO: 18-6-19  RoundEnvironment 表示当前的运行环境 用于查找对应的注解
    private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
        // TODO: 18-6-4 之所以使用 LinkedHashMap 在于其具有arraylist的有序以及hashmap的查询快的优势
        Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
        Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();

        // TODO: 18-6-19 处理注解
        // Process each @BindAnim element.
        for (Element element : env.getElementsAnnotatedWith(BindAnim.class)) {
            if (!SuperficialValidation.validateElement(element)) continue;
            try {
                parseResourceAnimation(element, builderMap, erasedTargetNames);
            } catch (Exception e) {
                logParsingError(element, BindAnim.class, e);
            }
        }

      ......... //省略部分代码

        // TODO: 18-6-19 对BindView此注解的处理
        // Process each @BindView element.
        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);
            }
        }

    ......... //省略部分代码

        // TODO: 18-6-19 处理事件
        // Process each annotation that corresponds to a listener.
        for (Class<? extends Annotation> listener : LISTENERS) {
            findAndParseListener(env, listener, builderMap, erasedTargetNames);
        }

        // Associate superclass binders with their subclass binders. This is a queue-based tree walk
        // which starts at the roots (superclasses) and walks to the leafs (subclasses).
        // TODO: 18-6-19 将Map.Entry<TypeElement, BindingSet.Builder>转化为Map<TypeElement, BindingSet>
        Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
                new ArrayDeque<>(builderMap.entrySet());
        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;
    }

ButterKnifeProcessor#parseBindView

 // TODO: 18-6-19 Element代表源代码 ;TypeElement代表源代码中的元素类型
    private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
                               Set<TypeElement> erasedTargetNames) {
        // TODO: 18-6-19 getEnclosingElement 获取父节点
        TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

        // TODO: 18-6-19 检查用户的合法性
        // Start by verifying common generated code restrictions.
        boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
                || isBindingInWrongPackage(BindView.class, element);

        // TODO: 18-6-19 TypeMirror用于获取类的信息
        // 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;
        }

        // TODO: 18-6-19 获取id
        // Assemble information on the field.
        int id = element.getAnnotation(BindView.class).value();
        // TODO: 18-6-19 从 builderMap中取出Builder 此处的每一个builder对应项目中的某一个类 比如绑定的是MainActivity 那么就是对应MainActivity
        // TODO: 18-6-19 BindingSet.Builder用来构建BindingSet和ViewBinding的中间产物
        BindingSet.Builder builder = builderMap.get(enclosingElement);
        Id resourceId = elementToId(element, BindView.class, id);
        if (builder != null) {
            // TODO: 18-6-19 检查是否已经存在此资源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 {
            // TODO: 18-6-19 创建builder 并且存进builderMap中
            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);
    }

ButterKnifeProcessor#isInaccessibleViaGeneratedCode

// TODO: 18-6-19 检查用户的合法性其中一种检验标准
    private boolean isInaccessibleViaGeneratedCode(Class<? extends Annotation> annotationClass,
                                                   String targetThing, Element element) {
        boolean hasError = false;
        // TODO: 18-6-19 获取父节点
        TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

        // TODO: 18-6-19 检查修饰符 如果包含PRIVATE或者STATIC就抛出异常
        // Verify field or method modifiers.
        Set<Modifier> modifiers = element.getModifiers();
        if (modifiers.contains(PRIVATE) || modifiers.contains(STATIC)) {
            error(element, "@%s %s must not be private or static. (%s.%s)",
                    annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
                    element.getSimpleName());
            hasError = true;
        }

        // TODO: 18-6-19 检查父节点是否是类
        // Verify containing type.
        if (enclosingElement.getKind() != CLASS) {
            error(enclosingElement, "@%s %s may only be contained in classes. (%s.%s)",
                    annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
                    element.getSimpleName());
            hasError = true;
        }

        // TODO: 18-6-19 检查父类是否是私有类
        // Verify containing class visibility is not private.
        if (enclosingElement.getModifiers().contains(PRIVATE)) {
            error(enclosingElement, "@%s %s may not be contained in private classes. (%s.%s)",
                    annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
                    element.getSimpleName());
            hasError = true;
        }

        return hasError;
    }

ButterKnifeProcessor#isBindingInWrongPackage

// TODO: 18-6-19 不能在sdk以及jdk的源码中使用
    private boolean isBindingInWrongPackage(Class<? extends Annotation> annotationClass,
                                            Element element) {
        TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
        String qualifiedName = enclosingElement.getQualifiedName().toString();

        // TODO: 18-6-19 筛选出sdk
        if (qualifiedName.startsWith("android.")) {
            error(element, "@%s-annotated class incorrectly in Android framework package. (%s)",
                    annotationClass.getSimpleName(), qualifiedName);
            return true;
        }
        // TODO: 18-6-19 筛选出jdk
        if (qualifiedName.startsWith("java.")) {
            error(element, "@%s-annotated class incorrectly in Java framework package. (%s)",
                    annotationClass.getSimpleName(), qualifiedName);
            return true;
        }

        return false;
    }

ButterKnifeProcessor#Builder

// TODO: 18-6-19 此类对应我们要生成代码所需要的所有信息 看名字就知道使用了建造者模式
  static final class Builder {
    // TODO: 18-6-19 有注解的对应类的类名
    private final TypeName targetTypeName;
    // TODO: 18-6-19 注解器生成的类的类名
    private final ClassName bindingClassName;
    // TODO: 18-6-19 标记是不是final view activity 以及dialog
    private final boolean isFinal;
    private final boolean isView;
    private final boolean isActivity;
    private final boolean isDialog;

    private BindingSet parentBinding;
    // TODO: 18-6-19 一个id对应一个 ViewBinding.Builder
    private final Map<Id, ViewBinding.Builder> viewIdMap = new LinkedHashMap<>();
    private final ImmutableList.Builder<FieldCollectionViewBinding> collectionBindings =
        ImmutableList.builder();
    private final ImmutableList.Builder<ResourceBinding> resourceBindings = ImmutableList.builder();

BindingSet#getOrCreateViewBindings

 private ViewBinding.Builder getOrCreateViewBindings(Id id) {
      ViewBinding.Builder viewId = viewIdMap.get(id);
      if (viewId == null) {
        // TODO: 18-6-25 构建Builder
        viewId = new ViewBinding.Builder(id);
        viewIdMap.put(id, viewId);
      }
      return viewId;
    }

到这里BindView分析完成


接下来分析事件的绑定

源码分析
Web note ad 1