美文网首页
Android ButterKnife 源码分析

Android ButterKnife 源码分析

作者: 是刘航啊 | 来源:发表于2021-03-17 11:56 被阅读0次
    ButterKnife 依赖
    implementation 'com.jakewharton:butterknife:10.2.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
    
    ButterKnife 使用
    public class MainActivity extends AppCompatActivity {
        @BindView(R.id.tv_test)
        TextView tvTest;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            tvTest.setText("test");
        }
    
        @OnClick(R.id.tv_test)
        public void tvTestClick(){
    
        }
    
    }
    
    源码解析 ButterKnife -> bind
    public static Unbinder bind(@NonNull Activity target) {
        View sourceView = target.getWindow().getDecorView();
        return bind(target, sourceView);
    }
    

    target.getWindow() 拿到的是 PhoneWindow 对象
    sourceView 是 PhoneWindow 通过 getDecorView() 方法拿到顶层的 DecorView
    bind() 将当前 Activity 和 DecorView 作为参数传递给 bind()

    bind
    public static Unbinder bind(@NonNull Object target, @NonNull View source) {
        Class<?> targetClass = target.getClass();
        if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
        Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
        
        if (constructor == null) {
          return Unbinder.EMPTY;
        }
        
        //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
        try {
          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);
        }
    }
    

    首先通过 Activity 拿到对应的 Class,其次通过 Class 拿到一个类型为 Unbinder 的 Constructor 构造器对象,最后通过 newInstance 反射得到 Unbinder 对象并返回。

    findBindingConstructorForClass
    private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
        Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
        if (bindingCtor != null || BINDINGS.containsKey(cls)) {
          if (debug) Log.d(TAG, "HIT: Cached in binding map.");
          return bindingCtor;
        }
        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 {
          Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
          //noinspection unchecked
          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;
    }
    

    首先会由 BINDINGS ( LinkedHashMap ) 通过 Class 获取对应的 Constructor 构造器对象,如果存在则直接返回

    解下来会通过 cls.getName() 获取 Activity 的类名,然后通过反射查找 ClassName_ViewBinding 的类 ( 例如 MainActivity_ViewBinding ) ,并通过这个类找到 Constructor 构造器对象,BINDINGS 加入缓存,最后返回这个构造器对象

    MainActivity_ViewBinding
    public class MainActivity_ViewBinding implements Unbinder {
        private MainActivity target;
        
        private View view7f08018e;
        @UiThread
        public MainActivity_ViewBinding(MainActivity target) {
            this(target, target.getWindow().getDecorView());
        }
      
        @UiThread
        public MainActivity_ViewBinding(final MainActivity target, View source) {
            this.target = target;
            View view;
            view = Utils.findRequiredView(source, R.id.tv_test, "field 'tvTest' and method 'tvTestClick'");
            target.tvTest = Utils.castView(view, R.id.tv_test, "field 'tvTest'", TextView.class);
            view7f08018e = view;
            view.setOnClickListener(new DebouncingOnClickListener() {
              @Override
              public void doClick(View p0) {
                target.tvTestClick();
              }
            });
        }
        
        @Override
        @CallSuper
        public void unbind() {
            MainActivity target = this.target;
            if (target == null) throw new IllegalStateException("Bindings already cleared.");
            this.target = null;
    
            target.tvTest = null;
    
            view7f08018e.setOnClickListener(null);
            view7f08018e = null;
        }
    }
    

    ButterKnife 中 bindingClass.getConstructor(cls, View.class) 对应的就是 MainActivity_ViewBinding 中两个参数的构造方法

     @UiThread
        public MainActivity_ViewBinding(final MainActivity target, View source) {
            this.target = target;
            View view;
            view = Utils.findRequiredView(source, R.id.tv_test, "field 'tvTest' and method 'tvTestClick'");
            target.tvTest = Utils.castView(view, R.id.tv_test, "field 'tvTest'", TextView.class);
            view7f08018e = view;
            view.setOnClickListener(new DebouncingOnClickListener() {
              @Override
              public void doClick(View p0) {
                target.tvTestClick();
              }
            });
        }
    
    Utils.findRequiredView
    public static View findRequiredView(View source, @IdRes int id, String who) {
        View view = source.findViewById(id);
        if (view != null) {
          return view;
        }
        String name = getResourceEntryName(source, id);
        throw new IllegalStateException("Required view '"
            + name
            + "' with ID "
            + id
            + " for "
            + who
            + " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"
            + " (methods) annotation.");
    }
    

    通过 findViewById 获取对应的 View

    Utils.castView
    public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) {
        try {
          return cls.cast(view);
        } catch (ClassCastException e) {
          String name = getResourceEntryName(view, id);
          throw new IllegalStateException("View '"
              + name
              + "' with ID "
              + id
              + " for "
              + who
              + " was of the wrong type. See cause for more info.", e);
        }
    }
    

    将得到的 View 转换成指定的 View

    public MainActivity_ViewBinding(final MainActivity target, View source) {
        ...
        View view;
        view = Utils.findRequiredView(source, R.id.tv_test, "field 'tvTest' and method 'tvTestClick'");
        target.tvTest = Utils.castView(view, R.id.tv_test, "field 'tvTest'", TextView.class);
        ...
    }
    

    通过 findRequiredView 方法拿到 R.id.tv_test 的 View,然后通过 castView 将 View 转换成 TextVIew

    MainActivity_ViewBinding 是如何创建的?首先我们需要了解 2 个知识点
    1. 注解( annotation )
    2. 注解处理器( annotationProcessor )
    注解( annotation )

    注解其实并不复杂,例如( @A「 A 洗脚店 」、@B「 B 洗脚店」与 @Target「使用范围:方法、变量等」、@Retention「生命周期:类、源文件等」)类似一个标签表明它的作用。给大家推荐一篇文章 注解( annotation )

    注解处理器( annotationProcessor )

    annotationProcessor 可以理解成一种处理注解的工具( 扫描检测 ),处理的核心就是 AbstractProcessor 。ButterKnife 中处注解的类为 ButterKnifeProcessor

    如何查看 ButterKnifeProcessor 类 ?
    1. 在 gradle 中将 annotationProcessor 改为 implementation,即可在 External Libraries 中查看源码
    2. 在 GitHub 中查看源码 ButterKnifeProcessor
    ButterKnifeProcessor
    @AutoService(Processor.class)
    public final class ButterKnifeProcessor extends AbstractProcessor {
        ...
        @Override public synchronized void init(ProcessingEnvironment env) { 
            ...
        }
    
        private Set<Class<? extends Annotation>> getSupportedAnnotations() {
            ...
        }
    
        @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
            ...
        }
        ...
    }
    
    ButterKnifeProcessor -> init
    @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) {
          }
        }
    }
    
    ProcessingEnvironment
    public interface ProcessingEnvironment {
        //选取重要的解读
        ...
        Elements getElementUtils();
        Types getTypeUtils();
        Filer getFiler();
        ...
    }
    
    • Elements getElementUtils 是处理 Element 的工具类,Element 可以理解成扫描到的源文件,源文件中独立的部分可以认作特立的 Element,如包( PackageElement )、类型( TypeElement )

    • Types getTypeUtils() 获取 TypeElement 的信息

    • Filer getFiler() 创建文件

    init 方法主要用来初始化工具类

    ButterKnifeProcessor -> getSupportedAnnotations
    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;
    }
    

    getSupportedAnnotations 返回支持注解的类型

    ButterKnifeProcessor -> process
    @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
        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(sdk, debuggable);
          try {
            javaFile.writeTo(filer);
          } catch (IOException e) {
            error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
          }
        }
    
        return false;
    }
    
    process -> findAndParseTargets
    private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
        ...
       
        // 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);
          }
        }
        
        ...
    }
    

    findAndParseTargets 处理了每个自定义的注解,虽然方法不一样,但是处理的逻辑是相同的,这里抽取 BindView 来分析

    findAndParseTargets -> parseBindView(上部分)
    private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
          Set<TypeElement> erasedTargetNames) {
        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) {
            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;
        }
        ...
    }
    
    findAndParseTargets -> parseBindView -> isInaccessibleViaGeneratedCode
    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();
        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;
        }
    
        // 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;
        }
    
        // 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;
    }
    

    验证当前类或者方法是否是 PRIVATE|STATIC,验证父类是否是 Class 且是否是 PRIVATE

    findAndParseTargets -> parseBindView -> isBindingInWrongPackage
    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;
    }
    

    验证当前方法是否以「 android. 」|「 java. 」开头

    以上很多验证信息都是验证 Element 是否继承自 View,如果不是继承自View 就不会走后面的流程
    findAndParseTargets -> parseBindView(下部分)
    private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
          Set<TypeElement> erasedTargetNames) {
        
        ...
        // Assemble information on the field.
        int id = element.getAnnotation(BindView.class).value();
        BindingSet.Builder builder = builderMap.get(enclosingElement);
        Id resourceId = elementToId(element, BindView.class, id);
        if (builder != null) {
          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 {
          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);
    }
    

    element.getAnnotation(BindView.class).value() 通过@BindView 注解获取到对应 View 的 id。通过 builderMap.get(enclosingElement) 根据我们对应的元素查找 Builder。如果存在,就会判断有没有重复的引用;如果不存在,就会去创建一个新的 Builder。最后通过 builder.addField() 添加到集合中

    findAndParseTargets -> parseBindView -> getOrCreateBindingBuilder
    private BindingSet.Builder getOrCreateBindingBuilder(
          Map<TypeElement, BindingSet.Builder> builderMap, TypeElement enclosingElement) {
        BindingSet.Builder builder = builderMap.get(enclosingElement);
        if (builder == null) {
          builder = BindingSet.newBuilder(enclosingElement);
          builderMap.put(enclosingElement, builder);
        }
        return builder;
    }
    
    getOrCreateBindingBuilder -> newBuilder
    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);
    }
    
    newBuilder -> getBindingClassName
    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");
    }
    

    BindingSet 就确定了生成 MainActivity_ViewBinding 类的信息

    findAndParseTargets -> parseBindView -> addField
    void addField(Id id, FieldViewBinding binding) {
        getOrCreateViewBindings(id).setFieldBinding(binding);
    }
    
    addField -> getOrCreateViewBindings
    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;
    }
    

    最终将 id 与 view 进行绑定,加入集合,findAndParseTargets 的流程就结束了

    ButterKnifeProcessor -> process
    @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
        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(sdk, debuggable);
          try {
            javaFile.writeTo(filer);
          } catch (IOException e) {
            error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
          }
        }
    
        return false;
    }
    
    process -> brewJava
    JavaFile brewJava(int sdk, boolean debuggable) {
        TypeSpec bindingConfiguration = createType(sdk, debuggable);
        return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
            .addFileComment("Generated code from Butter Knife. Do not modify!")
            .build();
    }
    
    brewJava -> createType
    private TypeSpec createType(int sdk, boolean debuggable) {
        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());
        } 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(sdk, debuggable));
    
        if (hasViewBindings() || parentBinding == null) {
          result.addMethod(createBindingUnbindMethod(result));
        }
    
        return result.build();
    }
    
    if (parentBinding != null) {
        result.superclass(parentBinding.getBindingClassName());
    } else {
        result.addSuperinterface(UNBINDER);
    }
    

    这段代码的意思就是如果存在父类就继承父类,如果不存在就实现 Unbinder 接口

    public class MainActivity_ViewBinding implements Unbinder {
    
    }
    

    上面的方法大致生成这段 Java 代码,下面重点看 createBindingConstructor() 创建绑定的构造器方法

    createBindingConstructor
    private MethodSpec createBindingConstructor(int sdk, boolean debuggable) {
        ...
        for (ViewBinding binding : viewBindings) {
            addViewBinding(constructor, binding, debuggable);
        }
        ...
    }
    
    addViewBinding
    private void addViewBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
        ...  
        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;
        }
        ...
    }
    
    static boolean requiresCast(TypeName type) {
        return !VIEW_TYPE.equals(type.toString());
    }
    

    测试代码中创建的为 TextView,所以 requiresCast 会返回 true,那么会执行 else 部分的代码

    • builder.add("$T.find", UTILS) -> UTILS.find
    • builder.add(fieldBinding.isRequired() ? "RequiredView" : "OptionalView") ->
      RequiredView
    • builder.add("(source, $L", binding.getId().code) -> (source,R.id.tv_test
    • builder.add(", $S", asHumanDescription(singletonList(fieldBinding))) -> , Description
    • builder.add(")") -> )

    完整的字符串就是 "UTILS.find" + "RequiredView" + " (source,R.id.tv_test" +", Description" +")" -> Utils.findRequiredView(source, R.id.tv_test, "Description"),MainActivity_ViewBinding 对应的就是 Utils.findRequiredView(source, R.id.tv_test, "field 'tvTest' and method 'tvTestClick'");

    process 流程就分析到这里
    参考文献

    ButterKnife 原理解析

    总结
    1. ButterKnife 通过注解处理器( annotationProcessor ),经过 ButterKnifeProcessor
    • init 初始工具类
    • getSupportedAnnotations 获取支持的注解列表
    • process 扫描处理所有支持的注解并加入集合,便利集合生成 java 代码,并将 java 代码生成对应的 class 文件
    1. 在 Activity 中使用 ButterKnife.bind(this) 方法,加载对应的 _ViewBinding 类,达道省去写 findViewById 的目的。
    ButterKnife 源码解析就介绍到这里了,如果有什么写得不对的,可以在下方评论留言,我会第一时间改正。

    相关文章

      网友评论

          本文标题:Android ButterKnife 源码分析

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