美文网首页
Butterknife源码详解

Butterknife源码详解

作者: kjy_112233 | 来源:发表于2019-01-25 11:11 被阅读0次

    一、源码解析

    (1)ButterKnife.bind(this)

      @NonNull @UiThread
      public static Unbinder bind(@NonNull Activity target) {
        //通过target获取当前xml布局
        View sourceView = target.getWindow().getDecorView();
        return bind(target, sourceView);
      }
    
      @NonNull @UiThread
      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());
        //通过class查找constructor即上文生成的classname_ViewBinding类
        Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
    
        if (constructor == null) {
          return Unbinder.EMPTY;
        }
        try {
          //调用newInstance构造方法构造一个Unbinder
          return constructor.newInstance(target, source);
        } 
        //code...
      }
    
      //缓存集合
      @VisibleForTesting
      static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>();
    
      @Nullable @CheckResult @UiThread
      private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
        //缓存的LinkedHashMap 查找当前的activity是否缓存,BINDINGS是绑定的缓存
        Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
        if (bindingCtor != null || BINDINGS.containsKey(cls)) {
          //如果有缓存直接返回
          return bindingCtor;
        }
    
        String clsName = cls.getName();
        //code...
        try {
          //通过反射去加载clsName + "_ViewBinding"这个类
          Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
          //noinspection unchecked
          bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
        } 
        //code...
        //获取到实例将它加入BINDINGS缓存并返回,建立缓存,会尽量减少性能损失
        BINDINGS.put(cls, bindingCtor);
        return bindingCtor;
      }
    

    (2)看看生成的MainActivity_ViewBinding.java

    public class MainActivity_ViewBinding implements Unbinder {
      private MainActivity target;
    
      private View view7f080046;
    
      @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;
        //findViewById
        target.textView = Utils.findRequiredViewAsType(source, R.id.textView, "field 'textView'", TextView.class);
        view = Utils.findRequiredView(source, R.id.button, "field 'button' and method 'onButton'");
        target.button = Utils.castView(view, R.id.button, "field 'button'", Button.class);
        view7f080046 = view;
        //点击事件
        view.setOnClickListener(new DebouncingOnClickListener() {
          @Override
          public void doClick(View p0) {
            target.onButton();
          }
        });
        target.imageView = Utils.findRequiredViewAsType(source, R.id.imageView, "field 'imageView'", ImageView.class);
        //资源引用
        Context context = source.getContext();
        Resources res = context.getResources();
        target.message = res.getString(R.string.name);
      }
    
      @Override
      @CallSuper
      public void unbind() {
        MainActivity target = this.target;
        if (target == null) throw new IllegalStateException("Bindings already cleared.");
        this.target = null;
    
        target.textView = null;
        target.button = null;
        target.imageView = null;
    
        view7f080046.setOnClickListener(null);
        view7f080046 = null;
      }
    }
    
    • MainActivity_ViewBinding解析
      public static <T> T findOptionalViewAsType(View source, @IdRes int id, String who,
          Class<T> cls) {
        View view = source.findViewById(id);
        return castView(view, id, who, cls);
      }
    
      public static View findRequiredView(View source, @IdRes int id, String who) {
        View view = source.findViewById(id);
        if (view != null) {
          return view;
        }
        //code...
      }
    
      public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) {
        try {
          //强制转换为控件类型
          return cls.cast(view);
        }
        //code...
      }
    

    (3)ButterKnifeProcessor解析

    • ButterKnife中使用了注解的解析处理器AbstractProcessor;AbstractProcessor是一个抽象类,该类实现了接口Processor;ButterKnife的注解处理器叫ButterKnifeProcessor
    • init
      @Override 
      public synchronized void init(ProcessingEnvironment env) {
        super.init(env);
        //code...
        //用来处理TypeMirror的工具类
        typeUtils = env.getTypeUtils();
        //用来创建生成辅助文件的工具类
        filer = env.getFiler();
        //code...
      }
    
    • getSupportedAnnotationTypes
      @Override 
      public Set<String> getSupportedAnnotationTypes() {
        //创建了一个LinkedHashSet对象
        Set<String> types = new LinkedHashSet<>();
        //将getSupportedAnnotations()方法返回的支持注解集合进行遍历添加到types中返回
        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
          types.add(annotation.getCanonicalName());
        }
        return types;
      }
    
    • getSupportedSourceVersion:注册了一系列的Bindxxx注解类和监听列表LISTENERS
      private Set<Class<? extends Annotation>> getSupportedAnnotations() {
        Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
        annotations.add(BindAnim.class);
        annotations.add(BindArray.class);
        annotations.add(BindBitmap.class);
        annotations.add(BindBool.class);
        annotations.add(BindColor.class);
        annotations.add(BindDimen.class);
        annotations.add(BindDrawable.class);
        annotations.add(BindFloat.class);
        annotations.add(BindFont.class);
        annotations.add(BindInt.class);
        annotations.add(BindString.class);
        annotations.add(BindView.class);
        annotations.add(BindViews.class);
        annotations.addAll(LISTENERS);
        return annotations;
      }
    
      private static final List<Class<? extends Annotation>> LISTENERS = Arrays.asList(
          OnCheckedChanged.class, 
          OnClick.class, 
          OnEditorAction.class, 
          OnFocusChange.class, 
          OnItemClick.class, 
          OnItemLongClick.class, 
          OnItemSelected.class, 
          OnLongClick.class, 
          OnPageChange.class, 
          OnTextChanged.class, 
          OnTouch.class 
      );
    
    • process
      @Override 
      public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
        //查找解析绑定元素:TypeElement:类、接口、Fragment;BindingSet:类绑定集合
        Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
        //遍历解析生成java
        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)生成了java源文件
            javaFile.writeTo(filer);
          } catch (IOException e) {
            error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
          }
        }
    
        return false;
      }
    
    • findAndParseTargets(env)
      private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
        Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
        Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
    
        //for循环code...   
    
        //一系列处理每一个@Bindxxx元素的for循环代码块
        for (Element element : env.getElementsAnnotatedWith(BindViews.class)) {
          try {
            parseBindViews(element, builderMap, erasedTargetNames);
          } catch (Exception e) {
            logParsingError(element, BindViews.class, e);
          }
        }
    
        for (Class<? extends Annotation> listener : LISTENERS) {
          findAndParseListener(env, listener, builderMap, erasedTargetNames);
        }
    
        Map<TypeElement, ClasspathBindingSet> classpathBindings =
            findAllSupertypeBindings(builderMap, erasedTargetNames);
    
        //将builderMap中的数据添加到队列中
        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();
          //获取集合中的key和value
          TypeElement type = entry.getKey();
          BindingSet.Builder builder = entry.getValue();
          //查找当前类元素的父类元素
          TypeElement parentType = findParentType(type, erasedTargetNames, classpathBindings.keySet());
          //判断是否存在父类元素,不存在直接构建builder放入bindingMap中;
          //存在将parentBinding添加到BindingSet.Builder这个建造者对象,然后创建BindingSet再添加到bindingMap中
          if (parentType == null) {
            bindingMap.put(type, builder.build());
          } else {
            BindingSet.Builder parentBinding = bindingMap.get(parentType);
            if (parentBinding == null) {
              parentBinding = classpathBindings.get(parentType);
            }
            if (parentBinding != null) {
              builder.setParent(parentBinding);
              bindingMap.put(type, builder.build());
            } else {
              entries.addLast(entry);
            }
          }
        }
    
        return bindingMap;
      }
    
    • binding.brewJava(sdk, debuggable)
    JavaFile brewJava(int sdk, boolean debuggable) {
        //bindingConfiguration保存了所有的绑定配置信息
        TypeSpec bindingConfiguration = createType(sdk, debuggable);
        //bindingConfiguration对象构建生成一个JavaFile对象
        return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
        .addFileComment("Generated code from Butter Knife. Do not modify!")
        .build();
    }
    
    • 在编译的时候扫描注解,并通过自定义的ButterKnifeProcessor做相应的处理解析得到bindingMap对象,最后调用 javapoet 库生成 java模板代码。
    • 当我们调用ButterKnife的bind()方法的时候,它会根据类的全限定类型,找到相应的模板代码,并在其中完成findViewById、setOnClick、setOnLongClick等操作。
    • ButterKnife不能将控件和方法设置为private或者static,是因为在className_ViewBinder类会直接调用该控件和方法进行赋值。
    • 使用编译时注解,在编译时期自动生成Unbinder(APT),这个对象自动生成了我们需要的动画、监听事件等等。

    相关文章

      网友评论

          本文标题:Butterknife源码详解

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