Android技术知识Android开发

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

2019-04-14  本文已影响10人  zl_adams

我们平时在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的实例对象,绑定页面的控件变量和对应的控件。

上一篇下一篇

猜你喜欢

热点阅读