美文网首页Android技术知识Android开发
浅析butterknife之页面控件绑定流程及源码分析

浅析butterknife之页面控件绑定流程及源码分析

作者: zl_adams | 来源:发表于2019-04-14 19:28 被阅读10次

    我们平时在activity使用butterknife时,会有以下类似代码
    (注解@BindView和 @OnClick都是用于生成MainActivity_ViewBinding内容的)

    public class MainActivity extends AppCompatActivity {
        @BindView(R.id.btn)
        Button btn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
        }
    
        @OnClick(R.id.btn)
        public void onViewClicked() {
            Toast.makeText(this, "点击了按钮", Toast.LENGTH_SHORT).show();
        }
    }
    

    查看ButterKnife类查看bind(this)方法

      @NonNull @UiThread
      public static Unbinder bind(@NonNull Activity target) {
        //内部经过LayoutInflater.from(context).inflate(R.layout.activity_main, viewGroup)得到的view
        View sourceView = target.getWindow().getDecorView();
        return createBinding(target, sourceView);
      }
    

    通过反射获取ViewBinding:MainActivity_ViewBinding,执行页面控件的绑定

      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());
        //这里获取到的是页面MainActivity对应的ViewBinding:MainActivity_ViewBinding(编译期生产的文件)的Constructor
        Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
    
        if (constructor == null) {
          return Unbinder.EMPTY;
        }
    
        //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
        try {
          //执行MainActivity_ViewBinding的构造方法,实现页面控件的绑定
          return constructor.newInstance(target, source);
        } 
        //省略一些非重点代码
        ...
      }
    

    查看findBindingConstructorForClass(Class<?> cls)如何返回MainActivity_ViewBinding的构造器方法Constructor

    @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.")) {
         if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
         return null;
       }  
       try {
         //重点在这里,通过MainActivity和_ViewBinding拼接,获取MainActivity_ViewBinding
         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;
     }
    

    真正实现控件绑定是在MainActivity_ViewBinding进行的

      @UiThread
     public MainActivity_ViewBinding(final MainActivity target, View source) {
       this.target = target;
    
       View view;
       //找到对应的控件
       view = Utils.findRequiredView(source, R.id.btn, "field 'btn' and method 'onViewClicked'");
       //转型
       target.btn = Utils.castView(view, R.id.btn, "field 'btn'", Button.class);
       view2131165218 = view;
       //设置点击监听
       view.setOnClickListener(new DebouncingOnClickListener() {
         @Override
         public void doClick(View p0) {
           target.onViewClicked();
         }
       });
     }
    

    总结整个流程:

    1、onCreated方法中执行ButterKnife.bind(this);
    2、获取setContentView(int layoutId)对应的View;
    3、根据bind()方法传递的Activity,通过字符串拼接“ clsName + "_ViewBinding" ”,获取到MainActivity_ViewBinding,进而获取到MainActivity_ViewBinding的Constructor;
    4、传入第二部获取到的View,执行MainActivity_ViewBinding的Constructor方法,创建MainActivity_ViewBinding的实例对象,绑定页面的控件变量和对应的控件。

    相关文章

      网友评论

        本文标题:浅析butterknife之页面控件绑定流程及源码分析

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