annotationProcessor

1. annotationProcessor 메모 프로세서는 컴파일 단계에서 메모를 처리하는 데 사용됩니다.그것을 통해 컴파일할 때 필요한 코드를 자동으로 생성할 수 있는데, 모두 이렇게 사용하는 것 같다.
ButterKnife
1.bind는 Unbinder 인스턴스의 구성 방법을 찾아 Unbinder 인스턴스를 생성하는 것입니다.
  @NonNull @UiThread
  public static Unbinder bind(@NonNull Activity target) {
    View sourceView = target.getWindow().getDecorView();
    return createBinding(target, sourceView);
  }
 private static Unbinder createBinding(@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);
    }
  }

2. Unbinder 인스턴스는 어떻게 작동합니까?
public class MainActivity_ViewBinding implements Unbinder
 @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;
    target.mSlMain = Utils.findRequiredViewAsType(source, R.id.sl_main, "field 'mSlMain'", VerticalDrawerLayout.class);
    view = Utils.findRequiredView(source, R.id.ll_bottom_bar, "field 'mLlBottomBar' and method 'onViewClicked'");
    target.mLlBottomBar = Utils.castView(view, R.id.ll_bottom_bar, "field 'mLlBottomBar'", LinearLayout.class);
    view2131296444 = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.onViewClicked(p0);
      }
    });
 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.");
  }

구조 방법에서activity의 속성에 직접적으로 값을 부여하는 것을 볼 수 있다. 이것은bindView가 주해한 구성원 변수가 사유가 될 수 없는 이유를 이해할 수 있다.
3. Unbinder 구현 클래스는 어디에서 왔습니까?
@AutoService(Processor.class)
@SuppressWarnings("NullAway") // TODO fix all these...
public final class ButterKnifeProcessor extends AbstractProcessor 

관건은 바로 아래의 이 방법 중입니다.
@Override public boolean process(Set extends TypeElement> elements, RoundEnvironment env) {
    Map bindingMap = findAndParseTargets(env);

    for (Map.Entry 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;
  }
package butterknife.compiler;

import butterknife.OnTouch;
import butterknife.internal.ListenerClass;
import butterknife.internal.ListenerMethod;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.AnnotationSpec;
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 com.squareup.javapoet.WildcardTypeName;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

import static butterknife.compiler.ButterKnifeProcessor.ACTIVITY_TYPE;
import static butterknife.compiler.ButterKnifeProcessor.DIALOG_TYPE;
import static butterknife.compiler.ButterKnifeProcessor.VIEW_TYPE;
import static butterknife.compiler.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 {
  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 RESOURCES = ClassName.get("android.content.res", "Resources");
  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 SUPPRESS_LINT =
      ClassName.get("android.annotation", "SuppressLint");
  private static final ClassName UNBINDER = ClassName.get("butterknife", "Unbinder");
  static final ClassName BITMAP_FACTORY = ClassName.get("android.graphics", "BitmapFactory");
  static final ClassName CONTEXT_COMPAT =
      ClassName.get("androidx.core.content", "ContextCompat");
  static final ClassName ANIMATION_UTILS =
          ClassName.get("android.view.animation", "AnimationUtils");

  private final TypeName targetTypeName;
  private final ClassName bindingClassName;
  private final boolean isFinal;
  private final boolean isView;
  private final boolean isActivity;
  private final boolean isDialog;
  private final ImmutableList viewBindings;
  private final ImmutableList collectionBindings;
  private final ImmutableList resourceBindings;
  private final @Nullable BindingSet parentBinding;

  private BindingSet(TypeName targetTypeName, ClassName bindingClassName, boolean isFinal,
      boolean isView, boolean isActivity, boolean isDialog, ImmutableList viewBindings,
      ImmutableList collectionBindings,
      ImmutableList resourceBindings, @Nullable BindingSet parentBinding) {
    this.isFinal = isFinal;
    this.targetTypeName = targetTypeName;
    this.bindingClassName = bindingClassName;
    this.isView = isView;
    this.isActivity = isActivity;
    this.isDialog = isDialog;
    this.viewBindings = viewBindings;
    this.collectionBindings = collectionBindings;
    this.resourceBindings = resourceBindings;
    this.parentBinding = parentBinding;
  }

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

  private TypeSpec createType(int sdk, boolean debuggable) {
    TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
        .addModifiers(PUBLIC);
    if (isFinal) {
      result.addModifiers(FINAL);
    }

    if (parentBinding != null) {
      result.superclass(parentBinding.bindingClassName);
    } 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();
  }

  private MethodSpec createBindingViewDelegateConstructor() {
    return MethodSpec.constructorBuilder()
        .addJavadoc("@deprecated Use {@link #$T($T, $T)} for direct creation.
" + "Only present for runtime invocation through {@code ButterKnife.bind()}.
", 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(int sdk, boolean debuggable) { 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 (hasUnqualifiedResourceBindings()) { // Aapt can change IDs out from underneath us, just suppress since all will work at runtime. constructor.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType") .build()); } if (hasOnTouchMethodBindings()) { constructor.addAnnotation(AnnotationSpec.builder(SUPPRESS_LINT) .addMember("value", "$S", "ClickableViewAccessibility") .build()); } 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("
"); } if (hasTargetField()) { constructor.addStatement("this.target = target"); constructor.addCode("
"); } 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, debuggable); } for (FieldCollectionViewBinding binding : collectionBindings) { constructor.addStatement("$L", binding.render(debuggable)); } if (!resourceBindings.isEmpty()) { constructor.addCode("
"); } } if (!resourceBindings.isEmpty()) { if (constructorNeedsView()) { constructor.addStatement("$T context = source.getContext()", CONTEXT); } if (hasResourceBindingsNeedingResource(sdk)) { constructor.addStatement("$T res = context.getResources()", RESOURCES); } for (ResourceBinding binding : resourceBindings) { constructor.addStatement("$L", binding.render(sdk)); } } return constructor.build(); } private MethodSpec createBindingUnbindMethod(TypeSpec.Builder bindingClass) { 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("
"); for (ViewBinding binding : viewBindings) { if (binding.getFieldBinding() != null) { result.addStatement("target.$L = null", binding.getFieldBinding().getName()); } } for (FieldCollectionViewBinding binding : collectionBindings) { result.addStatement("target.$L = null", binding.name); } } if (hasMethodBindings()) { result.addCode("
"); for (ViewBinding binding : viewBindings) { addFieldAndUnbindStatement(bindingClass, result, binding); } } if (parentBinding != null) { result.addCode("
"); result.addStatement("super.unbind()"); } return result.build(); } private void addFieldAndUnbindStatement(TypeSpec.Builder result, MethodSpec.Builder unbindMethod, ViewBinding bindings) { // Only add fields to the binding if there are method bindings. Map>> classMethodBindings = bindings.getMethodBindings(); if (classMethodBindings.isEmpty()) { return; } String fieldName = bindings.isBoundToRoot() ? "viewSource" : "view" + Integer.toHexString(bindings.getId().value); result.addField(VIEW, fieldName, PRIVATE); // We only need to emit the null check if there are zero required bindings. boolean needsNullChecked = bindings.getRequiredBindings().isEmpty(); if (needsNullChecked) { unbindMethod.beginControlFlow("if ($N != null)", fieldName); } for (ListenerClass listenerClass : classMethodBindings.keySet()) { // We need to keep a reference to the listener // in case we need to unbind it via a remove method. boolean requiresRemoval = !"".equals(listenerClass.remover()); String listenerField = "null"; if (requiresRemoval) { TypeName listenerClassName = bestGuess(listenerClass.type()); listenerField = fieldName + ((ClassName) listenerClassName).simpleName(); result.addField(listenerClassName, listenerField, PRIVATE); } String targetType = listenerClass.targetType(); if (!VIEW_TYPE.equals(targetType)) { unbindMethod.addStatement("(($T) $N).$N($N)", bestGuess(targetType), fieldName, removerOrSetter(listenerClass, requiresRemoval), listenerField); } else { unbindMethod.addStatement("$N.$N($N)", fieldName, removerOrSetter(listenerClass, requiresRemoval), listenerField); } if (requiresRemoval) { unbindMethod.addStatement("$N = null", listenerField); } } unbindMethod.addStatement("$N = null", fieldName); if (needsNullChecked) { unbindMethod.endControlFlow(); } } private String removerOrSetter(ListenerClass listenerClass, boolean requiresRemoval) { return requiresRemoval ? listenerClass.remover() : listenerClass.setter(); } private void addViewBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) { 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 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>> 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>> e : classMethodBindings.entrySet()) { ListenerClass listener = e.getKey(); Map> 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 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 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(");
"); } } if (!"void".equals(method.returnType()) && !hasReturnValue) { builder.add("return $L;
", 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 getListenerMethods(ListenerClass listener) { if (listener.method().length == 1) { return Arrays.asList(listener.method()); } try { List 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(); } } private static TypeName bestGuess(String type) { switch (type) { case "void": return TypeName.VOID; case "boolean": return TypeName.BOOLEAN; case "byte": return TypeName.BYTE; case "char": return TypeName.CHAR; case "double": return TypeName.DOUBLE; case "float": return TypeName.FLOAT; case "int": return TypeName.INT; case "long": return TypeName.LONG; case "short": return TypeName.SHORT; default: int left = type.indexOf(' typeArguments = new ArrayList<>(); do { typeArguments.add(WildcardTypeName.subtypeOf(Object.class)); left = type.indexOf(' viewIdMap = new LinkedHashMap<>(); private final ImmutableList.Builder collectionBindings = ImmutableList.builder(); private final ImmutableList.Builder resourceBindings = ImmutableList.builder(); private Builder(TypeName targetTypeName, ClassName bindingClassName, boolean isFinal, boolean isView, boolean isActivity, boolean isDialog) { this.targetTypeName = targetTypeName; this.bindingClassName = bindingClassName; this.isFinal = isFinal; this.isView = isView; this.isActivity = isActivity; this.isDialog = isDialog; } void addField(Id id, FieldViewBinding binding) { getOrCreateViewBindings(id).setFieldBinding(binding); } void addFieldCollection(FieldCollectionViewBinding binding) { collectionBindings.add(binding); } boolean addMethod( Id id, ListenerClass listener, ListenerMethod method, MethodViewBinding binding) { ViewBinding.Builder viewBinding = getOrCreateViewBindings(id); if (viewBinding.hasMethodBinding(listener, method) && !"void".equals(method.returnType())) { return false; } viewBinding.addMethodBinding(listener, method, binding); return true; } void addResource(ResourceBinding binding) { resourceBindings.add(binding); } void setParent(BindingSet parent) { this.parentBinding = parent; } @Nullable String findExistingBindingName(Id id) { ViewBinding.Builder builder = viewIdMap.get(id); if (builder == null) { return null; } FieldViewBinding fieldBinding = builder.fieldBinding; if (fieldBinding == null) { return null; } return fieldBinding.getName(); } 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 viewBindings = ImmutableList.builder(); for (ViewBinding.Builder builder : viewIdMap.values()) { viewBindings.add(builder.build()); } return new BindingSet(targetTypeName, bindingClassName, isFinal, isView, isActivity, isDialog, viewBindings.build(), collectionBindings.build(), resourceBindings.build(), parentBinding); } } }

javapoet을 이용하여 대응하는 Unbinder class를 생성하는 원리는 매우 간단하다.
요약:
1. 컴파일할 때 @BindView 또는 프로세스에 추가된 다른 주석을 만나면 대응하는 Unbinder 구현 클래스 파일을 생성합니다
2. ButterKinfe 호출bind 방법은 Unbinder의 구조 방법을 조회하고 Unbinder를 실례화합니다. Unbinder의 구조 방법에서 주석이 귀속된view id->findviewById를 초기화합니다.
3. 난점:javapoet 생성 실현 클래스

좋은 웹페이지 즐겨찾기