| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
Ari8006
8年前发布

ButterKnife源码简析

   <p>ButterKnife 是一个 Android 视图快速注入库,它通过给 View 字段添加注解,可以让我们丢掉 findViewById() 来获取 View 的方法,从而简化了代码。</p>    <h2>编译时注解</h2>    <h3>概述</h3>    <p>编译时注解的核心依赖 APT ( Annotation Processing Tools ) 实现,原理是在某些代码元素上(如类型、函数、字段等)添加注解,在编译时编译器会检查 AbstractProcessor 的子类,并且调用该类型的 process 函数,然后将添加了注解的所有元素都传递到 process 函数中,使得开发人员可以在编译器进行相应的处理,例如,根据注解生成新的Java类,这也就是EventBus,Retrofit,Dragger等开源库的基本原理。</p>    <h3>创建</h3>    <p>创建一个 Java Library</p>    <p>创建一个 annotationcompiler 的 java module</p>    <p>配置 gradle</p>    <pre>  <code class="language-java">sourceCompatibility = "1.7"  targetCompatibility = "1.7"  </code></pre>    <p>同时需要在 app 的 module 中配置一下 Java7</p>    <pre>  <code class="language-java">compileOptions {     sourceCompatibility JavaVersion.VERSION_1_7     targetCompatibility JavaVersion.VERSION_1_7  }  </code></pre>    <p>创建 Annotation</p>    <pre>  <code class="language-java">package com.yydcdut.annotation;    public @interface InjectView {      int value();  }  </code></pre>    <p>创建 AbstractProcessor</p>    <pre>  <code class="language-java">package com.yydcdut.process;    @SupportedAnnotationTypes("com.yydcdut.annotation.InjectView")  public class ViewInjectProcessor extends AbstractProcessor {          /**       * 初始化       *       * @param processingEnvironment 提供了一些实用的工具类Elements, Types和Filer       */      @Override      public synchronized void init(ProcessingEnvironment processingEnvironment) {          super.init(processingEnvironment);      }          /**       * 指定使用的java版本       *       * @return       */      @Override      public SourceVersion getSupportedSourceVersion() {          return SourceVersion.latestSupported();      }          /**       * 指定哪些注解应该被注解处理器注册       *       * @return       */      @Override      public Set<String> getSupportedAnnotationTypes() {          return super.getSupportedAnnotationTypes();      }          /**       * 主要的逻辑处理       *       * @param set       * @param env       * @return       */      @Override      public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {          return false;      }  }  </code></pre>    <p>注册处理器</p>    <p>在 java module 里的 main 目录中创建一个 resources 文件夹,然后下边在创建 META-INF/services ,创建一个 javax.annotation.processing.Processor 文件,在此文件中写入注解处理器的类全称</p>    <pre>  <code class="language-java">com.yydcdut.process.ViewInjectProcessor  </code></pre>    <p>添加 android-apt</p>    <p>在 project 下的 build.gradle 中添加 apt 插件</p>    <pre>  <code class="language-java">classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'  </code></pre>    <p>然后在 app 中的 build.gradle 添加 apt 插件</p>    <pre>  <code class="language-java">apply plugin: 'com.android.application'  apply plugin: 'com.neenbedankt.android-apt'  </code></pre>    <p>添加依赖</p>    <p>在 app 中的 build.gradle 添加依赖</p>    <pre>  <code class="language-java">dependencies {      compile project(':annotationcompiler')  }  </code></pre>    <p>Code</p>    <pre>  <code class="language-java">@SupportedAnnotationTypes("com.yydcdut.annotation.InjectView")  public class ViewInjectProcessor extends AbstractProcessor {      @Override      public Set<String> getSupportedAnnotationTypes() {          Set<String> types = new LinkedHashSet<String>();          types.add(InjectView.class.getCanonicalName());          return types;      }          @Override      public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {          generateCode(set, env);          return true;      }          private void generateCode(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {          StringBuilder builder = new StringBuilder()                  .append("package com.yydcdut.test.generated;\n\n")                  .append("public class GeneratedClass {\n\n") // open class                  .append("\tpublic String getMessage() {\n") // open method                  .append("\t\treturn \"");          // for each javax.lang.model.element.Element annotated with the CustomAnnotation          for (Element element : roundEnv.getElementsAnnotatedWith(InjectView.class)) {              int id = element.getAnnotation(InjectView.class).value();              builder.append(id + "\n");          }          builder.append("\";\n") // end return                  .append("\t}\n") // close method                  .append("}\n"); // close class              try { // write the file              JavaFileObject source = processingEnv.getFiler().createSourceFile("com.yydcdut.test.generated.GeneratedClass");                  Writer writer = source.openWriter();              writer.write(builder.toString());              writer.flush();              writer.close();          } catch (IOException e) {              // Note: calling e.printStackTrace() will print IO errors              // that occur from the file already existing after its first run, this is normal              e.printStackTrace();          }      }  }  </code></pre>    <p>被注解的部分代码:</p>    <pre>  <code class="language-java">package com.yydcdut.textdemo;    public class MainActivity extends Activity {      @InjectView(value = 123)      private EditText mEditText;  }  </code></pre>    <p>编译之后查看 app/build/generated/source/apt/debug/com/yydcdut/test/generated/GeneratedClass.java</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/06197d3569225380fbe77307679b75b3.png"></p>    <h2>源码解析</h2>    <h3>ButterKnifeProcessor</h3>    <p>这里只分析 BindView 的过程</p>    <p>ButterKnifeProcessor</p>    <p>根据上述创建注解器的流程,那么我们分析源码也有了一个流程,就直接看 ButterKnife 的 AbstractProcessor :</p>    <pre>  <code class="language-java">@AutoService(Processor.class)//自动注册处理器  public final class ButterKnifeProcessor extends AbstractProcessor {          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 //      );          private Elements elementUtils;//处理Element的工具类      private Types typeUtils;//处理TypeMirror的工具类      private Filer filer;//可以创建.java文件      @Override      public synchronized void init(ProcessingEnvironment env) {          super.init(env);          //......          elementUtils = env.getElementUtils();          typeUtils = env.getTypeUtils();          filer = env.getFiler();          //......      }          @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(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(BindInt.class);          annotations.add(BindString.class);          annotations.add(BindView.class);          annotations.add(BindViews.class);          annotations.addAll(LISTENERS);            return annotations;      }          @Override      public SourceVersion getSupportedSourceVersion() {          return SourceVersion.latestSupported();      }          @Override      public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {          Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);////查找所有的被注解,封装成BindingSet保存到map中            //遍历步map的生成.java文件          for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {              TypeElement typeElement = entry.getKey();              BindingSet binding = entry.getValue();                JavaFile javaFile = binding.brewJava(sdk);              try {                  javaFile.writeTo(filer);              } catch (IOException e) {                  error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());              }          }            return false;      }  }  </code></pre>    <p>在 process 中主要做了两件事,第一是找出被注解的元素,封装成 Map<TypeElement, BindingSet> ,第一个参数可以想成类,第二个参数可以想成所有有关信息的封装;第二就是遍历这个 map ,然后针对每个类生成对应的 ViewBinder 类。</p>    <p>ButterKnifeProcessor#findAndParseTargets</p>    <p>那么主要的查找工作在 findAndParseTargets() 中进行:</p>    <pre>  <code class="language-java">public final class ButterKnifeProcessor extends AbstractProcessor {        private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {          Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();          Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();          // .......                   // Process each @BindView element.          for (Element element : env.getElementsAnnotatedWith(BindView.class)) {//拿到被BindView注解的元素              // 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);              }          }          // .......                  return bindingMap;      }  }  </code></pre>    <p>RoundEnvironment 可以理解成查询注解信息的类,而 Element 可以理解成程序中的元素,比如包、类、方法等等。</p>    <pre>  <code class="language-java">public class ClassA { // TypeElement      private int var; // VariableElement        public ClassA() {// ExecuteableElement      }        public void setA(int newA // TypeElement      ) {      }  }  </code></pre>    <p>TypeElement 代表源代码中元素类型,但是 TypeElement 并不包含类的相关信息,可以从 TypeElement 获取类的名称,但不能获取类的信息,比如说父类。这些信息可以通过 TypeMirror 获取。你可以通过调用 element.asType() 来获取一个 Element 的 TypeMirror 。</p>    <p>ButterKnifeProcessor#parseBindView</p>    <pre>  <code class="language-java">public final class ButterKnifeProcessor extends AbstractProcessor {      private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap, Set<TypeElement> erasedTargetNames) {          TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();//类 com.yydcdut.textdemo.MainActivity            // 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();//android.widget.EditText          if (elementType.getKind() == TypeKind.TYPEVAR) {//false (elementType.getKind()==DECLARED)              TypeVariable typeVariable = (TypeVariable) elementType;              elementType = typeVariable.getUpperBound();          }          Name qualifiedName = enclosingElement.getQualifiedName();//com.yydcdut.textdemo.MainActivity          Name simpleName = element.getSimpleName();//mEditText          if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {//必须为view类型的子类或者是接口              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;          }            // Assemble information on the field.          int id = element.getAnnotation(BindView.class).value();//得到id            BindingSet.Builder builder = builderMap.get(enclosingElement);          QualifiedId qualifiedId = elementToQualifiedId(element, id);//封装成QualifiedId          if (builder != null) {              String existingBindingName = builder.findExistingBindingName(getId(qualifiedId));              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);//为这个类创建BindingSet.Builder          }            String name = simpleName.toString();//mEditText          TypeName type = TypeName.get(elementType);//android.widget.EditText          boolean required = isFieldRequired(element);            builder.addField(getId(qualifiedId), new FieldViewBinding(name, type, required));//封装后放入builder中            // Add the type-erased version to the valid binding targets set.          erasedTargetNames.add(enclosingElement);      }          /**       * 判断修饰符等       *       * @param annotationClass       * @param targetThing       * @param element       * @return       */      private boolean isInaccessibleViaGeneratedCode(Class<? extends Annotation> annotationClass, String targetThing, Element element) {          boolean hasError = false;          TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();            // Verify method modifiers.          Set<Modifier> modifiers = element.getModifiers();          if (modifiers.contains(PRIVATE) || modifiers.contains(STATIC)) {//不能是private或者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) {//不能是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)) {//不能是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;      }        /**       * 判断是否弄错了包名       * 弄成android或者java的       *       * @param annotationClass       * @param element       * @return       */      private boolean isBindingInWrongPackage(Class<? extends Annotation> annotationClass, Element element) {          TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();          String qualifiedName = enclosingElement.getQualifiedName().toString();//com.yydcdut.textdemo.MainActivity            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;      }          /**       * 是不是otherType的子类型       *       * @param typeMirror       * @param otherType       * @return       */      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;      }          /**       * 是不是接口       *       * @param typeMirror       * @return       */      private boolean isInterface(TypeMirror typeMirror) {          return typeMirror instanceof DeclaredType                  && ((DeclaredType) typeMirror).asElement().getKind() == INTERFACE;      }          private QualifiedId elementToQualifiedId(Element element, int id) {          return new QualifiedId(elementUtils.getPackageOf(element).getQualifiedName().toString(), id);      }          /**       * 判断有没有被Nullable修饰       *       * @param element       * @return       */      private static boolean isFieldRequired(Element element) {          return !hasAnnotationWithName(element, NULLABLE_ANNOTATION_NAME);      }        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;      }  }  </code></pre>    <p>这里是解析被 BindView 修饰的元素,将信息都封装成 BindingSet.Builder 。</p>    <p>BindingSet#newBuilder</p>    <pre>  <code class="language-java">final class BindingSet {          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<ViewBinding> viewBindings;      private final ImmutableList<FieldCollectionViewBinding> collectionBindings;      private final ImmutableList<ResourceBinding> resourceBindings;      private final BindingSet parentBinding;        private BindingSet(TypeName targetTypeName, ClassName bindingClassName, boolean isFinal,                         boolean isView, boolean isActivity, boolean isDialog, ImmutableList<ViewBinding> viewBindings,                         ImmutableList<FieldCollectionViewBinding> collectionBindings,                         ImmutableList<ResourceBinding> resourceBindings, 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;      }        static Builder newBuilder(TypeElement enclosingElement) {          TypeMirror typeMirror = enclosingElement.asType();//android.widget.EditText            boolean isView = isSubtypeOfType(typeMirror, VIEW_TYPE);//是不是View          boolean isActivity = isSubtypeOfType(typeMirror, ACTIVITY_TYPE);//是不是Activity          boolean isDialog = isSubtypeOfType(typeMirror, DIALOG_TYPE);//是不是Dialog            TypeName targetType = TypeName.get(typeMirror);          if (targetType instanceof ParameterizedTypeName) {              targetType = ((ParameterizedTypeName) targetType).rawType;          }            String packageName = getPackage(enclosingElement).getQualifiedName().toString();//com.yydcdut.textdemo          String className = enclosingElement.getQualifiedName().toString().substring(                  packageName.length() + 1).replace('.', '$');//MainActivity          ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding");//com.yydcdut.textdemo.MainActivity_ViewBinding            boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL);//是不是final修饰的          return new Builder(targetType, bindingClassName, isFinal, isView, isActivity, isDialog);      }          static final class Builder {                  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 Map<Id, ViewBinding.Builder> viewIdMap = new LinkedHashMap<>();            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);          }                  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, isFinal, isView, isActivity, isDialog, viewBindings.build(), collectionBindings.build(), resourceBindings.build(), parentBinding);          }      }  }  </code></pre>    <p>ButterKnifeProcessor#findAndParseTargets</p>    <pre>  <code class="language-java">public final class ButterKnifeProcessor extends AbstractProcessor {      @Override      public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {          Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();          //....                  //....          // 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).          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();//com.yydcdut.textdemo.MainActivity              BindingSet.Builder builder = entry.getValue();                TypeElement parentType = findParentType(type, erasedTargetNames);//父类              if (parentType == null) {//自己就是了                  bindingMap.put(type, builder.build());              } else {                  BindingSet parentBinding = bindingMap.get(parentType);//拿到父类的BindingSet                  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;      }         /**       * Finds the parent binder type in the supplied set, if any.       */      private TypeElement findParentType(TypeElement typeElement, Set<TypeElement> parents) {          TypeMirror type;          while (true) {              type = typeElement.getSuperclass();              if (type.getKind() == TypeKind.NONE) {                  return null;              }              typeElement = (TypeElement) ((DeclaredType) type).asElement();              if (parents.contains(typeElement)) {                  return typeElement;              }          }      }  }  </code></pre>    <p>ButterKnifeProcessor#process</p>    <p>findAndParseTargets 算是解析完了,再回过头来看第二步,生成文件的步骤</p>    <pre>  <code class="language-java">public final class ButterKnifeProcessor extends AbstractProcessor {      @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);//变成JavaFile              try {                  javaFile.writeTo(filer);//生成文件              } catch (IOException e) {                  error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());              }          }            return false;      }  }  </code></pre>    <p>将信息转变成 JavaFile ,通过 filer 完成。</p>    <p>write过程</p>    <pre>  <code class="language-java">final class BindingSet {      JavaFile brewJava(int sdk) {          return JavaFile.builder(bindingClassName.packageName(), createType(sdk))//packageName-->com.yydcdut.textdemo                  .addFileComment("Generated code from Butter Knife. Do not modify!")                  .build();      }          private TypeSpec createType(int sdk) {          TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())                  .addModifiers(PUBLIC);//申明类          if (isFinal) {              result.addModifiers(FINAL);//添加final关键字          }            if (parentBinding != null) {              result.superclass(parentBinding.bindingClassName);//继承父类          } else {              result.addSuperinterface(UNBINDER);//实现Unbinder接口          }            if (hasTargetField()) {              result.addField(targetTypeName, "target", PRIVATE);//设置private的target变量          }            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));            if (hasViewBindings() || parentBinding == null) {              result.addMethod(createBindingUnbindMethod(result));          }            return result.build();      }          private MethodSpec createBindingConstructorForView() {          MethodSpec.Builder builder = MethodSpec.constructorBuilder()//创建构造器                  .addAnnotation(UI_THREAD)//添加UIThread的注解                  .addModifiers(PUBLIC)//为public                  .addParameter(targetTypeName, "target");//构造函数中得有target参数          if (constructorNeedsView()) {//构造函数中的方法              builder.addStatement("this(target, target)");          } else {              builder.addStatement("this(target, target.getContext())");          }          return builder.build();      }        /**       * True if this binding requires a view. Otherwise only a context is needed.       */      private boolean constructorNeedsView() {          return hasViewBindings() //                  || parentBinding != null && parentBinding.constructorNeedsView();      }          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 createBindingConstructor(int sdk) {          MethodSpec.Builder constructor = MethodSpec.constructorBuilder()//构造方法                  .addAnnotation(UI_THREAD)//UIThread注解                  .addModifiers(PUBLIC);//public            if (hasMethodBindings()) {              constructor.addParameter(targetTypeName, "target", FINAL);//final的target变量          } else {              constructor.addParameter(targetTypeName, "target");          }            if (constructorNeedsView()) {              constructor.addParameter(VIEW, "source");//变量名为source的View          } else {              constructor.addParameter(CONTEXT, "context");//变量名为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) {//如果有父类,则调用super              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");//赋值              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);//View的初始化              }              for (FieldCollectionViewBinding binding : collectionBindings) {                  constructor.addStatement("$L", binding.render());//资源的初始化              }                if (!resourceBindings.isEmpty()) {                  constructor.addCode("\n");              }          }            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));//通过不同的SDK版本实现Resource相关(Drawable,Color等)的初始化              }          }            return constructor.build();      }          private void addViewBinding(MethodSpec.Builder result, ViewBinding binding) {          if (binding.isSingleFieldBinding()) {//只有findView的情况,没有监听器              // Optimize the common case where there's a single binding directly to a field.              FieldViewBinding fieldBinding = binding.getFieldBinding();              CodeBlock.Builder builder = CodeBlock.builder()                      .add("target.$L = ", fieldBinding.getName());//(target-->MainActivity) ($L-->mEditText)                boolean requiresCast = requiresCast(fieldBinding.getType());//是否需要强转              if (!requiresCast && !fieldBinding.isRequired()) {                  builder.add("source.findViewById($L)", binding.getId().code);//findViewById              } 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(")");//target.mEditText = Utils.findRequiredViewAsType(source, R.id.edit, "field 'mEditText'", EditText.class);              }              result.addStatement("$L", builder.build());              return;          }            List<MemberViewBinding> requiredBindings = binding.getRequiredBindings();          if (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));//view = Utils.findRequiredView(source, R.id.hello, "field 'hello', method 'sayHello', and method 'sayGetOffMe'");          }            addFieldBinding(result, binding);//强转          addMethodBindings(result, binding);//各种监听器      }        private void addFieldBinding(MethodSpec.Builder result, ViewBinding binding) {          FieldViewBinding fieldBinding = binding.getFieldBinding();          if (fieldBinding != null) {              if (requiresCast(fieldBinding.getType())) {                  result.addStatement("target.$L = $T.castView(view, $L, $S, $T.class)",                          fieldBinding.getName(), UTILS, binding.getId().code,                          asHumanDescription(singletonList(fieldBinding)), fieldBinding.getRawType());// target.hello = Utils.castView(view, R.id.hello, "field 'hello'", Button.class);              } else {                  result.addStatement("target.$L = view", fieldBinding.getName());              }          }      }  }  </code></pre>    <p>最后文件的生成是通过 com.squareup.javapoet.JavaFile 生成的。</p>    <p>官方 demo 的栗子</p>    <pre>  <code class="language-java">package com.example.butterknife.library;    import android.app.Activity;  import android.os.Bundle;  import android.support.annotation.NonNull;  import android.view.View;  import android.view.animation.AlphaAnimation;  import android.widget.Button;  import android.widget.ListView;  import android.widget.TextView;  import android.widget.Toast;    import java.util.List;    import butterknife.BindView;  import butterknife.BindViews;  import butterknife.ButterKnife;  import butterknife.OnClick;  import butterknife.OnItemClick;  import butterknife.OnLongClick;    import static android.widget.Toast.LENGTH_SHORT;    public class SimpleActivity extends Activity {      private static final ButterKnife.Action<View> ALPHA_FADE = new ButterKnife.Action<View>() {          @Override          public void apply(@NonNull View view, int index) {              AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);              alphaAnimation.setFillBefore(true);              alphaAnimation.setDuration(500);              alphaAnimation.setStartOffset(index * 100);              view.startAnimation(alphaAnimation);          }      };        @BindView(R2.id.title)      TextView title;      @BindView(R2.id.subtitle)      TextView subtitle;      @BindView(R2.id.hello)      Button hello;      @BindView(R2.id.list_of_things)      ListView listOfThings;      @BindView(R2.id.footer)      TextView footer;        @BindViews({R2.id.title, R2.id.subtitle, R2.id.hello})      List<View> headerViews;        private SimpleAdapter adapter;        @OnClick(R2.id.hello)      void sayHello() {          Toast.makeText(this, "Hello, views!", LENGTH_SHORT).show();          ButterKnife.apply(headerViews, ALPHA_FADE);      }        @OnLongClick(R2.id.hello)      boolean sayGetOffMe() {          Toast.makeText(this, "Let go of me!", LENGTH_SHORT).show();          return true;      }        @OnItemClick(R2.id.list_of_things)      void onItemClick(int position) {          Toast.makeText(this, "You clicked: " + adapter.getItem(position), LENGTH_SHORT).show();      }        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.simple_activity);          ButterKnife.bind(this);            // Contrived code to use the bound fields.          title.setText("Butter Knife");          subtitle.setText("Field and method binding for Android views.");          footer.setText("by Jake Wharton");          hello.setText("Say Hello");            adapter = new SimpleAdapter(this);          listOfThings.setAdapter(adapter);      }  }  </code></pre>    <p>官方 demo 的 apt 生成类的栗子</p>    <pre>  <code class="language-java">// Generated code from Butter Knife. Do not modify!  package com.example.butterknife.library;    import android.support.annotation.CallSuper;  import android.support.annotation.UiThread;  import android.view.View;  import android.widget.AdapterView;  import android.widget.Button;  import android.widget.ListView;  import android.widget.TextView;  import butterknife.Unbinder;  import butterknife.internal.DebouncingOnClickListener;  import butterknife.internal.Utils;  import java.lang.IllegalStateException;  import java.lang.Override;    public class SimpleActivity_ViewBinding<T extends SimpleActivity> implements Unbinder {    protected T target;      private View view2130968578;      private View view2130968579;      @UiThread    public SimpleActivity_ViewBinding(final T target, View source) {      this.target = target;        View view;      target.title = Utils.findRequiredViewAsType(source, R.id.title, "field 'title'", TextView.class);      target.subtitle = Utils.findRequiredViewAsType(source, R.id.subtitle, "field 'subtitle'", TextView.class);      view = Utils.findRequiredView(source, R.id.hello, "field 'hello', method 'sayHello', and method 'sayGetOffMe'");      target.hello = Utils.castView(view, R.id.hello, "field 'hello'", Button.class);      view2130968578 = view;      view.setOnClickListener(new DebouncingOnClickListener() {        @Override        public void doClick(View p0) {          target.sayHello();        }      });      view.setOnLongClickListener(new View.OnLongClickListener() {        @Override        public boolean onLongClick(View p0) {          return target.sayGetOffMe();        }      });      view = Utils.findRequiredView(source, R.id.list_of_things, "field 'listOfThings' and method 'onItemClick'");      target.listOfThings = Utils.castView(view, R.id.list_of_things, "field 'listOfThings'", ListView.class);      view2130968579 = view;      ((AdapterView<?>) view).setOnItemClickListener(new AdapterView.OnItemClickListener() {        @Override        public void onItemClick(AdapterView<?> p0, View p1, int p2, long p3) {          target.onItemClick(p2);        }      });      target.footer = Utils.findRequiredViewAsType(source, R.id.footer, "field 'footer'", TextView.class);      target.headerViews = Utils.listOf(          Utils.findRequiredView(source, R.id.title, "field 'headerViews'"),           Utils.findRequiredView(source, R.id.subtitle, "field 'headerViews'"),           Utils.findRequiredView(source, R.id.hello, "field 'headerViews'"));    }      @Override    @CallSuper    public void unbind() {      T target = this.target;      if (target == null) throw new IllegalStateException("Bindings already cleared.");        target.title = null;      target.subtitle = null;      target.hello = null;      target.listOfThings = null;      target.footer = null;      target.headerViews = null;        view2130968578.setOnClickListener(null);      view2130968578.setOnLongClickListener(null);      view2130968578 = null;      ((AdapterView<?>) view2130968579).setOnItemClickListener(null);      view2130968579 = null;        this.target = null;    }  }  </code></pre>    <h3>ButterKnife</h3>    <p>当要进行 bind 的时候,都需要在 Activity 或 View 等的初始化函数中进行绑定,这里就拿 Activity 为栗子进行分析:</p>    <pre>  <code class="language-java">public final class ButterKnife {      /**       * BindView annotated fields and methods in the specified {@link Activity}. The current content       * view is used as the view root.       *       * @param target Target activity for view binding.       */      @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);          }      }          @Nullable      @CheckResult      @UiThread      private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {          Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);//判断绑定过了没有          if (bindingCtor != null) {              if (debug) {                  Log.d(TAG, "HIT: Cached in binding map.");              }              return bindingCtor;          }          String clsName = cls.getName();          if (clsName.startsWith("android.") || clsName.startsWith("java.")) {//排除android和java的包              if (debug) {                  Log.d(TAG, "MISS: Reached framework class. Abandoning search.");              }              return null;          }          try {              Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");//加载后面加"_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;      }  }  </code></pre>    <p>最终通过 ClassLoader 的方式将类加载出来,最后 constructor.newInstance 方法来调用该类的构造函数。</p>    <h2>总结</h2>    <p>平时了解到更多的是运行时注解,即声明注解的生命周期为 RUNTIME ,然后在运行的时候通过反射完成注入,这种方式虽然简单,但是设计到比较多的反射,必然多多少少会有性能的损耗。而 ButterKnife 用的 APT 编译时解析技术,比较好的解决了反射这些问题。</p>    <p>APT 大概就是声明的注解的生命周期为 CLASS ,然后继承 AbstractProcessor 类。继承这个类后,在编译的时候,编译器会扫描所有带有你要处理的注解的类,然后再调用 AbstractProcessor 的 process 方法,对注解进行处理,那么就可以在处理的时候,动态生成绑定事件或者控件的 java 代码,然后在运行的时候,直接调用 bind 方法完成绑定。</p>    <p>其实这种方式的好处是我们不用再一遍一遍地写 findViewById 和 onClick 等代码了,这个框架在编译的时候帮我们自动生成了这些代码,然后在运行的时候调用就行了。</p>    <p> </p>    <p>来自:http://yydcdut.com/2017/04/19/butterknife-analyse/</p>    <p> </p>    
 本文由用户 Ari8006 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1492658301737.html
Java Android开发 移动开发 butterknife