当前位置: 首页>后端>正文

Android仿butterKnife,练习APT技术(三)

先对AbstractProcessor有个认识,每一个处理器都是继承于AbstractProcessor,如下所示:

package com.example.butterknife_complier;

public class ButterKnifeProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment env){ }

    @Override
    public boolean process(Set<extends TypeElement> annoations, RoundEnvironment env) { }

    @Override
    public Set<String> getSupportedAnnotationTypes() { }

    @Override
    public SourceVersion getSupportedSourceVersion() { }

}

? init(ProcessingEnvironment env): 输入ProcessingEnviroment参数,ProcessingEnviroment提供很多有用的工具类Elements, Types和Filer。
? process(Set<extends TypeElement> annotations, RoundEnvironment env): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。
? getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。
? getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。
完整代码如下:

package com.example.butterknife_complier;
import com.example.butterknife_annotations.BindView;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeName;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.tools.Diagnostic;


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

    private Filer filer;
    private Messager messager;
    static final String VIEW_TYPE = "android.view.View";
    static final String ACTIVITY_TYPE = "android.app.Activity";
    static final String DIALOG_TYPE = "android.app.Dialog";

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnvironment.getFiler();
        messager = processingEnvironment.getMessager(); //这里初始化打印message的工具messager
        messager.printMessage(Diagnostic.Kind.MANDATORY_WARNING,"processor init");
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(BindView.class.getCanonicalName()); // 返回支持的注解的集合
        return types;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<extends TypeElement> set, RoundEnvironment env) {
        messager.printMessage(Diagnostic.Kind.WARNING,"processor process env" + env.toString());
        Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
        for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
            TypeElement typeElement = entry.getKey();
            BindingSet binding = entry.getValue();
            JavaFile javaFile = binding.brewJava();  
            try {
                javaFile.writeTo(filer);
            } catch (IOException e) {
                messager.printMessage(Diagnostic.Kind.ERROR, String.format("Unable to write binding for type %s: %s", typeElement, e.getMessage()), typeElement);
            }
        }
        return false;
    }

    private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
        Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
        for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
            try {
                parseBindView(element, builderMap);   //这里只查找BindView注解
            } catch (Exception e) {
                logParsingError(element, BindView.class, e);
            }
        }
        Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
        for (Map.Entry<TypeElement, BindingSet.Builder> entry : builderMap.entrySet()) {
            TypeElement type = entry.getKey();
            BindingSet.Builder builder = entry.getValue();
            bindingMap.put(type, builder.build());
        }
        return bindingMap;
    }

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

        // 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) {
                error(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;
        }

        // Assemble information on the field.
        int id = element.getAnnotation(BindView.class).value();
        BindingSet.Builder builder = builderMap.get(enclosingElement);
        if (builder == null) {
            builder = BindingSet.newBuilder(enclosingElement);
            builderMap.put(enclosingElement, builder);
        }

        String name = simpleName.toString();
        TypeName type = TypeName.get(elementType);

//        TypeMirror typeMirror = enclosingElement.asType();
//        boolean isView = isSubtypeOfType(typeMirror, VIEW_TYPE);
//        boolean isActivity = isSubtypeOfType(typeMirror, ACTIVITY_TYPE);
//        boolean isDialog = isSubtypeOfType(typeMirror, DIALOG_TYPE);
//        messager.printMessage(Diagnostic.Kind.ERROR,"enclosingElement:" + typeMirror.toString() + "isView:" + isView + ",isActivity:" + isActivity + ",isDialog:" + isDialog);
        builder.addField(new Id(id), new FieldViewBinding(name, type, true));
    }

    private boolean isInaccessibleViaGeneratedCode(Class<extends Annotation> annotationClass, String targetThing, Element element) {
        boolean hasError = false;
        TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

        // Verify field or method modifiers.
        Set<Modifier> modifiers = element.getModifiers();
        //这里验证属性不能是private或者static修饰
        if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.STATIC)) {
            error(element, "@%s %s must not be private or static. (%s.%s)",
                    annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
                    element.getSimpleName());
            hasError = true;
        }

        // Verify containing type.
        if (enclosingElement.getKind() != ElementKind.CLASS) {
            error(enclosingElement, "@%s %s may only be contained in classes. (%s.%s)",
                    annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
                    element.getSimpleName());
            hasError = true;
        }

        // Verify containing class visibility is not private.
        if (enclosingElement.getModifiers().contains(Modifier.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;
    }

    private boolean isBindingInWrongPackage(Class<extends Annotation> annotationClass, Element element) {
        TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
        String qualifiedName = enclosingElement.getQualifiedName().toString();

        if (qualifiedName.startsWith("android.")) {
            error(element, "@%s-annotated class incorrectly in Android framework package. (%s)",
                    annotationClass.getSimpleName(), qualifiedName);
            return true;
        }
        if (qualifiedName.startsWith("java.")) {
            error(element, "@%s-annotated class incorrectly in Java framework package. (%s)",
                    annotationClass.getSimpleName(), qualifiedName);
            return true;
        }
        return false;
    }

    private boolean isInterface(TypeMirror typeMirror) {
        return typeMirror instanceof DeclaredType
                && ((DeclaredType) typeMirror).asElement().getKind() == ElementKind.INTERFACE;
    }

    static boolean isSubtypeOfType(TypeMirror typeMirror, String otherType) {
        if (isTypeEqual(typeMirror, otherType)) {
            return true;
        }
        if (typeMirror.getKind() != TypeKind.DECLARED) {
            return false;
        }
        DeclaredType declaredType = (DeclaredType) typeMirror;
        List<extends TypeMirror> typeArguments = declaredType.getTypeArguments();
        if (typeArguments.size() > 0) {
            StringBuilder typeString = new StringBuilder(declaredType.asElement().toString());
            typeString.append('<');
            for (int i = 0; i < typeArguments.size(); i++) {
                if (i > 0) {
                    typeString.append(',');
                }
                typeString.append('?');
            }
            typeString.append('>');
            if (typeString.toString().equals(otherType)) {
                return true;
            }
        }
        Element element = declaredType.asElement();
        if (!(element instanceof TypeElement)) {
            return false;
        }
        TypeElement typeElement = (TypeElement) element;
        TypeMirror superType = typeElement.getSuperclass();
        if (isSubtypeOfType(superType, otherType)) {
            return true;
        }
        for (TypeMirror interfaceType : typeElement.getInterfaces()) {
            if (isSubtypeOfType(interfaceType, otherType)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isTypeEqual(TypeMirror typeMirror, String otherType) {
        return otherType.equals(typeMirror.toString());
    }

    private void error(Element element, String errorMsg, Object... args) {
        messager.printMessage(Diagnostic.Kind.ERROR, String.format(errorMsg, args), element);
    }

    private void logParsingError(Element element, Class<extends Annotation> annotation, Exception e) {
        StringWriter stackTrace = new StringWriter();
        e.printStackTrace(new PrintWriter(stackTrace));
        error(element, "Unable to parse @%s binding.\n\n%s", annotation.getSimpleName(), stackTrace);
    }

}

1.通过messager打印的日志可以在build里面看到如下:


Android仿butterKnife,练习APT技术(三),第1张

也可以帮我们查看扫描注解的结果,以及错误信息

  1. Elements 和 TypeMirrors
    Element代表程序的元素,例如包、类或者方法。每个Element代表一个静态的、语言级别的构件。然而,TypeElement并不包含类本身的信息。你可以从TypeElement中获取类的名字,但是你获取不到类的信息,例如它的父类。这种信息需要通过TypeMirror获取。你可以通过调用elements.asType()获取元素的TypeMirror。
    Element的类型可以通过element.getKind()获取,TypeKind时一个枚举类:
public enum TypeKind {
    BOOLEAN,
    BYTE,
    SHORT,
    INT,
    LONG,
    CHAR,
    FLOAT,
    DOUBLE,
    VOID,
    NONE,
    NULL,
    ARRAY,
    DECLARED,
    ERROR,
    TYPEVAR,
    WILDCARD,
    PACKAGE,
    EXECUTABLE,
    OTHER,
    UNION,
    INTERSECTION;

    private TypeKind() {
    }

    public boolean isPrimitive() {
        switch(this) {
        case BOOLEAN:
        case BYTE:
        case SHORT:
        case INT:
        case LONG:
        case CHAR:
        case FLOAT:
        case DOUBLE:
            return true;
        default:
            return false;
        }
    }
}

举例:

package com.example;    // PackageElement

public class Foo {        // TypeElement

    private int a;      // VariableElement
    private Foo other;  // VariableElement

    public Foo () {}    // ExecuteableElement

    public void setA (  // ExecuteableElement
                     int newA   // VariableElement
                     ) {}
}

parseBindView里面有一些验证:比如属性不能时private或者static的,当我在text View前面加上private:

    @BindView(R.id.sample_text)
    private TextView textView;

点击运行,报错如下:


Android仿butterKnife,练习APT技术(三),第2张

还有其他,如不能被包含在private修改的类型,注解的对象必须是View的子类或者是一个接口类型。
验证通过,生成一个FieldViewBinding对象,记录要绑定的属性信息。
3.生成ViewBinding类

package com.example.butterknife_complier;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import static com.example.butterknife_complier.ButterKnifeProcessor.ACTIVITY_TYPE;
import static com.example.butterknife_complier.ButterKnifeProcessor.DIALOG_TYPE;
import static com.example.butterknife_complier.ButterKnifeProcessor.VIEW_TYPE;
import static com.example.butterknife_complier.ButterKnifeProcessor.isSubtypeOfType;
import static com.google.auto.common.MoreElements.getPackage;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;

/**
 * A set of all the bindings requested by a single type.
 */
final class BindingSet implements BindingInformationProvider {
    static final ClassName UTILS = ClassName.get("butterknife.internal", "Utils");
    private static final ClassName VIEW = ClassName.get("android.view", "View");
    private static final ClassName CONTEXT = ClassName.get("android.content", "Context");
    private static final ClassName UI_THREAD =
            ClassName.get("androidx.annotation", "UiThread");
    private static final ClassName CALL_SUPER = ClassName.get("androidx.annotation", "CallSuper");

    private static final ClassName UNBINDER = ClassName.get("butterknife", "Unbinder");

    private final TypeName targetTypeName;
    private final ClassName bindingClassName;
    private final TypeElement enclosingElement;
    private final boolean isFinal;
    private final boolean isView;
    private final boolean isActivity;
    private final boolean isDialog;
    private final ImmutableList<ViewBinding> viewBindings;
    private final @Nullable
    BindingInformationProvider parentBinding;
    private boolean debuggable = true;

    private BindingSet(
            TypeName targetTypeName, ClassName bindingClassName, TypeElement enclosingElement,
            boolean isFinal, boolean isView, boolean isActivity, boolean isDialog,
            ImmutableList<ViewBinding> viewBindings,
            @Nullable BindingInformationProvider parentBinding) {
        this.isFinal = isFinal;
        this.targetTypeName = targetTypeName;
        this.bindingClassName = bindingClassName;
        this.enclosingElement = enclosingElement;
        this.isView = isView;
        this.isActivity = isActivity;
        this.isDialog = isDialog;
        this.viewBindings = viewBindings;
        this.parentBinding = parentBinding;
    }

    @Override
    public ClassName getBindingClassName() {
        return bindingClassName;
    }

    JavaFile brewJava() {
        TypeSpec bindingConfiguration = createType(); //从这里开始生成配置信息
        return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
                .addFileComment("Generated code from Butter Knife. Do not modify!")
                .build();
    }

    private TypeSpec createType() {
        TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
                .addModifiers(PUBLIC)
                .addOriginatingElement(enclosingElement);
        if (isFinal) {
            result.addModifiers(FINAL);
        }

        if (parentBinding != null) {
            result.superclass(parentBinding.getBindingClassName());
        } else {
            result.addSuperinterface(UNBINDER);
        }

        if (hasTargetField()) {
            result.addField(targetTypeName, "target", PRIVATE);
        }

        if (isView) {
            result.addMethod(createBindingConstructorForView());
        } else if (isActivity) {
            result.addMethod(createBindingConstructorForActivity());//会走到这里,生成activity
        } else if (isDialog) {
            result.addMethod(createBindingConstructorForDialog());
        }
        if (!constructorNeedsView()) {
            // Add a delegating constructor with a target type + view signature for reflective use.
            result.addMethod(createBindingViewDelegateConstructor());
        }
        result.addMethod(createBindingConstructor());

        if (hasViewBindings() || parentBinding == null) {
            result.addMethod(createBindingUnbindMethod());  //然后到这里,初始化需要绑定的类
        }

        return result.build();
    }

    private MethodSpec createBindingViewDelegateConstructor() {
        return MethodSpec.constructorBuilder()
                .addJavadoc("@deprecated Use {@link #$T($T, $T)} for direct creation.\n    "
                                + "Only present for runtime invocation through {@code ButterKnife.bind()}.\n",
                        bindingClassName, targetTypeName, CONTEXT)
                .addAnnotation(Deprecated.class)
                .addAnnotation(UI_THREAD)
                .addModifiers(PUBLIC)
                .addParameter(targetTypeName, "target")
                .addParameter(VIEW, "source")
                .addStatement(("this(target, source.getContext())"))
                .build();
    }

    private MethodSpec createBindingConstructorForView() {
        MethodSpec.Builder builder = MethodSpec.constructorBuilder()
                .addAnnotation(UI_THREAD)
                .addModifiers(PUBLIC)
                .addParameter(targetTypeName, "target");
        if (constructorNeedsView()) {
            builder.addStatement("this(target, target)");
        } else {
            builder.addStatement("this(target, target.getContext())");
        }
        return builder.build();
    }

    private MethodSpec createBindingConstructorForActivity() {
        MethodSpec.Builder builder = MethodSpec.constructorBuilder()
                .addAnnotation(UI_THREAD)
                .addModifiers(PUBLIC)
                .addParameter(targetTypeName, "target");
        if (constructorNeedsView()) {
            builder.addStatement("this(target, target.getWindow().getDecorView())");
        } else {
            builder.addStatement("this(target, target)");
        }
        return builder.build();
    }

    private MethodSpec createBindingConstructorForDialog() {
        MethodSpec.Builder builder = MethodSpec.constructorBuilder()
                .addAnnotation(UI_THREAD)
                .addModifiers(PUBLIC)
                .addParameter(targetTypeName, "target");
        if (constructorNeedsView()) {
            builder.addStatement("this(target, target.getWindow().getDecorView())");
        } else {
            builder.addStatement("this(target, target.getContext())");
        }
        return builder.build();
    }

    private MethodSpec createBindingConstructor() {
        MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
                .addAnnotation(UI_THREAD)
                .addModifiers(PUBLIC);

        if (hasMethodBindings()) {
            constructor.addParameter(targetTypeName, "target", FINAL);
        } else {
            constructor.addParameter(targetTypeName, "target");
        }

        if (constructorNeedsView()) {
            constructor.addParameter(VIEW, "source");
        } else {
            constructor.addParameter(CONTEXT, "context");
        }

        if (parentBinding != null) {
            if (parentBinding.constructorNeedsView()) {
                constructor.addStatement("super(target, source)");
            } else if (constructorNeedsView()) {
                constructor.addStatement("super(target, source.getContext())");
            } else {
                constructor.addStatement("super(target, context)");
            }
            constructor.addCode("\n");
        }
        if (hasTargetField()) {
            constructor.addStatement("this.target = target");  //这里给target赋值
            constructor.addCode("\n");
        }

        if (hasViewBindings()) {
            if (hasViewLocal()) {
                // Local variable in which all views will be temporarily stored.
                constructor.addStatement("$T view", VIEW);
            }
            for (ViewBinding binding : viewBindings) {
                addViewBinding(constructor, binding);
            }
        }
        return constructor.build();
    }

    private MethodSpec createBindingUnbindMethod() {
        MethodSpec.Builder result = MethodSpec.methodBuilder("unbind")
                .addAnnotation(Override.class)
                .addModifiers(PUBLIC);
        if (!isFinal && parentBinding == null) {
            result.addAnnotation(CALL_SUPER);
        }

        if (hasTargetField()) {
            if (hasFieldBindings()) {
                result.addStatement("$T target = this.target", targetTypeName);
            }
            result.addStatement("if (target == null) throw new $T($S)", IllegalStateException.class,
                    "Bindings already cleared.");
            result.addStatement("$N = null", hasFieldBindings() "this.target" : "target");
            result.addCode("\n");
            for (ViewBinding binding : viewBindings) {
                if (binding.getFieldBinding() != null) {
                    result.addStatement("target.$L = null", binding.getFieldBinding().getName());
                }
            }
        }

        if (parentBinding != null) {
            result.addCode("\n");
            result.addStatement("super.unbind()");
        }
        return result.build();
    }


    private void addViewBinding(MethodSpec.Builder result, ViewBinding binding) {
        if (binding.isSingleFieldBinding()) {
            // Optimize the common case where there's a single binding directly to a field.
            FieldViewBinding fieldBinding = requireNonNull(binding.getFieldBinding());
            CodeBlock.Builder builder = CodeBlock.builder()
                    .add("target.$L = ", fieldBinding.getName());

            boolean requiresCast = requiresCast(fieldBinding.getType());
            if (!debuggable || (!requiresCast && !fieldBinding.isRequired())) {
                if (requiresCast) {
                    builder.add("($T) ", fieldBinding.getType());
                }
                builder.add("source.findViewById($L)", binding.getId().code);
            } else {
                builder.add("$T.find", UTILS);
                builder.add(fieldBinding.isRequired() "RequiredView" : "OptionalView");
                if (requiresCast) {
                    builder.add("AsType");
                }
                builder.add("(source, $L", binding.getId().code);
                if (fieldBinding.isRequired() || requiresCast) {
                    builder.add(", $S", asHumanDescription(singletonList(fieldBinding)));
                }
                if (requiresCast) {
                    builder.add(", $T.class", fieldBinding.getRawType());
                }
                builder.add(")");
            }
            result.addStatement("$L", builder.build());
            return;
        }

        List<MemberViewBinding> requiredBindings = binding.getRequiredBindings();
        if (!debuggable || requiredBindings.isEmpty()) {
            result.addStatement("view = source.findViewById($L)", binding.getId().code);
        } else if (!binding.isBoundToRoot()) {
            result.addStatement("view = $T.findRequiredView(source, $L, $S)", UTILS,
                    binding.getId().code, asHumanDescription(requiredBindings));
        }

        addFieldBinding(result, binding, debuggable);
        addMethodBindings(result, binding, debuggable);
    }

    private void addFieldBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
        FieldViewBinding fieldBinding = binding.getFieldBinding();
        if (fieldBinding != null) {
            if (requiresCast(fieldBinding.getType())) {
                if (debuggable) {
                    result.addStatement("target.$L = $T.castView(view, $L, $S, $T.class)",
                            fieldBinding.getName(), UTILS, binding.getId().code,
                            asHumanDescription(singletonList(fieldBinding)), fieldBinding.getRawType());
                } else {
                    result.addStatement("target.$L = ($T) view", fieldBinding.getName(),
                            fieldBinding.getType());
                }
            } else {
                result.addStatement("target.$L = view", fieldBinding.getName());
            }
        }
    }

    private void addMethodBindings(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
//        Map<ListenerClass, Map<ListenerMethod, Set<MethodViewBinding>>> classMethodBindings =
//                binding.getMethodBindings();
//        if (classMethodBindings.isEmpty()) {
//            return;
//        }
//
//        // We only need to emit the null check if there are zero required bindings.
//        boolean needsNullChecked = binding.getRequiredBindings().isEmpty();
//        if (needsNullChecked) {
//            result.beginControlFlow("if (view != null)");
//        }
//
//        // Add the view reference to the binding.
//        String fieldName = "viewSource";
//        String bindName = "source";
//        if (!binding.isBoundToRoot()) {
//            fieldName = "view" + Integer.toHexString(binding.getId().value);
//            bindName = "view";
//        }
//        result.addStatement("$L = $N", fieldName, bindName);
//
//        for (Map.Entry<ListenerClass, Map<ListenerMethod, Set<MethodViewBinding>>> e
//                : classMethodBindings.entrySet()) {
//            ListenerClass listener = e.getKey();
//            Map<ListenerMethod, Set<MethodViewBinding>> methodBindings = e.getValue();
//
//            TypeSpec.Builder callback = TypeSpec.anonymousClassBuilder("")
//                    .superclass(ClassName.bestGuess(listener.type()));
//
//            for (ListenerMethod method : getListenerMethods(listener)) {
//                MethodSpec.Builder callbackMethod = MethodSpec.methodBuilder(method.name())
//                        .addAnnotation(Override.class)
//                        .addModifiers(PUBLIC)
//                        .returns(bestGuess(method.returnType()));
//                String[] parameterTypes = method.parameters();
//                for (int i = 0, count = parameterTypes.length; i < count; i++) {
//                    callbackMethod.addParameter(bestGuess(parameterTypes[i]), "p" + i);
//                }
//
//                boolean hasReturnValue = false;
//                CodeBlock.Builder builder = CodeBlock.builder();
//                Set<MethodViewBinding> methodViewBindings = methodBindings.get(method);
//                if (methodViewBindings != null) {
//                    for (MethodViewBinding methodBinding : methodViewBindings) {
//                        if (methodBinding.hasReturnValue()) {
//                            hasReturnValue = true;
//                            builder.add("return "); // TODO what about multiple methods?
//                        }
//                        builder.add("target.$L(", methodBinding.getName());
//                        List<Parameter> parameters = methodBinding.getParameters();
//                        String[] listenerParameters = method.parameters();
//                        for (int i = 0, count = parameters.size(); i < count; i++) {
//                            if (i > 0) {
//                                builder.add(", ");
//                            }
//
//                            Parameter parameter = parameters.get(i);
//                            int listenerPosition = parameter.getListenerPosition();
//
//                            if (parameter.requiresCast(listenerParameters[listenerPosition])) {
//                                if (debuggable) {
//                                    builder.add("$T.castParam(p$L, $S, $L, $S, $L, $T.class)", UTILS,
//                                            listenerPosition, method.name(), listenerPosition, methodBinding.getName(), i,
//                                            parameter.getType());
//                                } else {
//                                    builder.add("($T) p$L", parameter.getType(), listenerPosition);
//                                }
//                            } else {
//                                builder.add("p$L", listenerPosition);
//                            }
//                        }
//                        builder.add(");\n");
//                    }
//                }
//
//                if (!"void".equals(method.returnType()) && !hasReturnValue) {
//                    builder.add("return $L;\n", method.defaultReturn());
//                }
//
//                callbackMethod.addCode(builder.build());
//                callback.addMethod(callbackMethod.build());
//            }
//
//            boolean requiresRemoval = listener.remover().length() != 0;
//            String listenerField = null;
//            if (requiresRemoval) {
//                TypeName listenerClassName = bestGuess(listener.type());
//                listenerField = fieldName + ((ClassName) listenerClassName).simpleName();
//                result.addStatement("$L = $L", listenerField, callback.build());
//            }
//
//            String targetType = listener.targetType();
//            if (!VIEW_TYPE.equals(targetType)) {
//                result.addStatement("(($T) $N).$L($L)", bestGuess(targetType), bindName,
//                        listener.setter(), requiresRemoval listenerField : callback.build());
//            } else {
//                result.addStatement("$N.$L($L)", bindName, listener.setter(),
//                        requiresRemoval listenerField : callback.build());
//            }
//        }
//
//        if (needsNullChecked) {
//            result.endControlFlow();
//        }
    }

//    private static List<ListenerMethod> getListenerMethods(ListenerClass listener) {
//        if (listener.method().length == 1) {
//            return Arrays.asList(listener.method());
//        }
//
//        try {
//            List<ListenerMethod> methods = new ArrayList<>();
//            Class<extends Enum<?>> callbacks = listener.callbacks();
//            for (Enum<?> callbackMethod : callbacks.getEnumConstants()) {
//                Field callbackField = callbacks.getField(callbackMethod.name());
//                ListenerMethod method = callbackField.getAnnotation(ListenerMethod.class);
//                if (method == null) {
//                    throw new IllegalStateException(String.format("@%s's %s.%s missing @%s annotation.",
//                            callbacks.getEnclosingClass().getSimpleName(), callbacks.getSimpleName(),
//                            callbackMethod.name(), ListenerMethod.class.getSimpleName()));
//                }
//                methods.add(method);
//            }
//            return methods;
//        } catch (NoSuchFieldException e) {
//            throw new AssertionError(e);
//        }
//    }

    static String asHumanDescription(Collection<extends MemberViewBinding> bindings) {
        Iterator<extends MemberViewBinding> iterator = bindings.iterator();
        switch (bindings.size()) {
            case 1:
                return iterator.next().getDescription();
            case 2:
                return iterator.next().getDescription() + " and " + iterator.next().getDescription();
            default:
                StringBuilder builder = new StringBuilder();
                for (int i = 0, count = bindings.size(); i < count; i++) {
                    if (i != 0) {
                        builder.append(", ");
                    }
                    if (i == count - 1) {
                        builder.append("and ");
                    }
                    builder.append(iterator.next().getDescription());
                }
                return builder.toString();
        }
    }

    /**
     * True when this type's bindings require a view hierarchy.
     */
    private boolean hasViewBindings() {
        return !viewBindings.isEmpty();
    }

    private boolean hasMethodBindings() {
//        for (ViewBinding bindings : viewBindings) {
//            if (!bindings.getMethodBindings().isEmpty()) {
//                return true;
//            }
//        }
        return false;
    }

    private boolean hasFieldBindings() {
        for (ViewBinding bindings : viewBindings) {
            if (bindings.getFieldBinding() != null) {
                return true;
            }
        }
        return false;
    }

    private boolean hasTargetField() {
        return hasFieldBindings() || hasMethodBindings();
    }

    private boolean hasViewLocal() {
        for (ViewBinding bindings : viewBindings) {
            if (bindings.requiresLocal()) {
                return true;
            }
        }
        return false;
    }

    /**
     * True if this binding requires a view. Otherwise only a context is needed.
     */
    @Override
    public boolean constructorNeedsView() {
        return hasViewBindings() //
                || (parentBinding != null && parentBinding.constructorNeedsView());
    }

    static boolean requiresCast(TypeName type) {
        return !VIEW_TYPE.equals(type.toString());
    }

    @Override
    public String toString() {
        return bindingClassName.toString();
    }

    static Builder newBuilder(TypeElement enclosingElement) {
        TypeMirror typeMirror = enclosingElement.asType();

        boolean isView = isSubtypeOfType(typeMirror, VIEW_TYPE);
        boolean isActivity = isSubtypeOfType(typeMirror, ACTIVITY_TYPE);
        boolean isDialog = isSubtypeOfType(typeMirror, DIALOG_TYPE);

        TypeName targetType = TypeName.get(typeMirror);
        if (targetType instanceof ParameterizedTypeName) {
            targetType = ((ParameterizedTypeName) targetType).rawType;
        }

        ClassName bindingClassName = getBindingClassName(enclosingElement);

        boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL);
        return new Builder(targetType, bindingClassName, enclosingElement, isFinal, isView, isActivity, isDialog);
    }

    static ClassName getBindingClassName(TypeElement typeElement) {
        String packageName = getPackage(typeElement).getQualifiedName().toString();
        String className = typeElement.getQualifiedName().toString().substring(
                packageName.length() + 1).replace('.', '$');
        return ClassName.get(packageName, className + "_ViewBinding");  //这里指定生成类名
    }

    static final class Builder {
        private final TypeName targetTypeName;
        private final ClassName bindingClassName;
        private final TypeElement enclosingElement;
        private final boolean isFinal;
        private final boolean isView;
        private final boolean isActivity;
        private final boolean isDialog;

        private @Nullable
        BindingInformationProvider parentBinding;

        private final Map<Id, ViewBinding.Builder> viewIdMap = new LinkedHashMap<>();

        private Builder(TypeName targetTypeName, ClassName bindingClassName, TypeElement enclosingElement,
                boolean isFinal, boolean isView, boolean isActivity, boolean isDialog) {
            this.targetTypeName = targetTypeName;
            this.bindingClassName = bindingClassName;
            this.enclosingElement = enclosingElement;
            this.isFinal = isFinal;
            this.isView = isView;
            this.isActivity = isActivity;
            this.isDialog = isDialog;
        }

        void addField(Id id, FieldViewBinding binding) {
            getOrCreateViewBindings(id).setFieldBinding(binding);
        }

        void setParent(BindingInformationProvider parent) {
            this.parentBinding = parent;
        }

        private ViewBinding.Builder getOrCreateViewBindings(Id id) {
            ViewBinding.Builder viewId = viewIdMap.get(id);
            if (viewId == null) {
                viewId = new ViewBinding.Builder(id);
                viewIdMap.put(id, viewId);
            }
            return viewId;
        }

        BindingSet build() {
            ImmutableList.Builder<ViewBinding> viewBindings = ImmutableList.builder();
            for (ViewBinding.Builder builder : viewIdMap.values()) {
                viewBindings.add(builder.build());
            }
            return new BindingSet(targetTypeName, bindingClassName, enclosingElement, isFinal, isView,
                    isActivity, isDialog, viewBindings.build(), parentBinding);
        }
    }
}

interface BindingInformationProvider {
    boolean constructorNeedsView();

    ClassName getBindingClassName();
}

执行过程后面加了注释
代码地址:https://gitee.com/sunshuo1092373331/apt.git


https://www.xamrdz.com/backend/3es1941387.html

相关文章: