美文网首页
Butterknife源码分析

Butterknife源码分析

作者: 34sir | 来源:发表于2019-01-29 10:14 被阅读4次

    目标

    • 实现原理
    • 设计模式

    分析

    核心模块.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分析完成


    接下来分析事件的绑定

    相关文章

      网友评论

          本文标题:Butterknife源码分析

          本文链接:https://www.haomeiwen.com/subject/csayyftx.html