ButterKnife的一些见解
程序员文章站
2022-04-20 22:05:06
...
前言:
作为一个程序员,我很关心国家,希望中美贸易成功,国家更加开放。
本讲解的ButterKnife 是基于10.1版本的,其他版本源码一样。
ButterKnife用法很简单
- 在Activity中
ButterKnife.bind(this); //this 是Activity
- 在fragment中
ButterKnife.bind(this,view); //this是当前的fragment 对象,view当前View的布局
- 自定义View
ButterKnife.bind(this,view);
或者
ButterKnife.bind(this);
Activity的讲解
ButterKnife 是采用编译时注解的,所以在编译后会生成编译好的文件
public abstract class MainActivity <T extends BasePresenter> extends RxFragmentActivity { private Unbinder bind; public T mPresenter; public Context mContext; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getLayoutId() != 0) { setContentView(getLayoutId()); } bind = ButterKnife.bind(this); //绑定
点击进入源码ButterKnife.bind
@NonNull @UiThread public static Unbinder bind(@NonNull Activity target) { View sourceView = target.getWindow().getDecorView(); //获取当前Activity的布局的最高的父布局 return bind(target, sourceView); //调用ButterKnife的bind()方法 }
发现调用了ButterKnife 的bind(target,sourceView)
点击进入源码
步骤1
/** * BindView annotated fields and methods in the specified {@code target} using the {@code source} * {@link View} as the view root. * * @param target Target class for view binding. * @param source View root on which IDs will be looked up. */ @NonNull @UiThread public static Unbinder bind(@NonNull Object target, @NonNull View source) { Class<?> targetClass = target.getClass(); //获取类对象(通过Class类对象可以获取类中许多信息,反射的原理) if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName()); Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); //传入类对象,生成一个Constructor 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); } }
发现源码中主要的代码为 findBindingConstructorForClass(targetClass)
步骤2
@Nullable @CheckResult @UiThread private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) { Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls); BINDINGS是一个LinkedHashMap这一步的作用是查看是否有Constructor对象,有的话直接返回,省的浪费资源 if (bindingCtor != null || BINDINGS.containsKey(cls)) { if (debug) Log.d(TAG, "HIT: Cached in binding map."); return bindingCtor; } String clsName = cls.getName(); if (clsName.startsWith("android.") || clsName.startsWith("java.") //判断是不是系统的类,如果是直接返回null || clsName.startsWith("androidx.")) { if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search."); return null; } try { Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding"); //根据当前类重新生成一个类名为 class.name + _ViewBinding的类名的类对象(clsName + "_ViewBinding" 这个类在编译的时候就已经存在了,编译注解) //关于如何获取类对象这里有三个方法 //方法一: Class a = 对象.getClass() //方法二: Class b = 类.class; //方法三: Class c = Class.forName(String ClassName); //方法四:(不建议使用) Class d = context.getClassLoader().loadClass(String ClassName) //noinspection unchecked bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class); //最后用新的 Class 创建一个 继承了Unbinder的 Constructor,并添加到BINDINGS : 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); //将新生成的对象添加到BINDINGS return bindingCtor; }
返回到步骤1中
发现最终返回的是
所以最终bind()
方法返回的是MainActivity_ViewBinding
类的实例
MainActivity_ViewBinding
的实例,那MainActivity_ViewBinding
这个类肯定是存在的。可以在如下目录找到它
我们看下这个类中做了什么操作
public class MainActivity_ViewBinding implements Unbinder { private MainActivity target; private View view7f07004c; @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; view = Utils.findRequiredView(source, R.id.img, "field 'img' and method 'onViewClicked'"); target.img = Utils.castView(view, R.id.img, "field 'img'", ImageView.class); view7f07004c = view; view.setOnClickListener(new DebouncingOnClickListener() { @Override public void doClick(View p0) { target.onViewClicked(); } }); } @Override @CallSuper public void unbind() { MainActivity target = this.target; if (target == null) throw new IllegalStateException("Bindings already cleared."); this.target = null; target.img = null; view7f07004c.setOnClickListener(null); view7f07004c = null; }
这类中持有了MainActivity的引用,所以无论如何在Activity销毁的时候一定要解绑
自定义VIew该如何解绑呢?
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
View inflate = LayoutInflater.from(context).inflate(R.layout.myview, this, true);
bind = ButterKnife.bind(this,inflate);
bind = ButterKnife.bind(inflate);//这个两个方法的效果是一样的,可以查看源码,单个参数的实际是调用了两个参数的
借鉴文章:
ButterKnife 原理解析
上一篇: Window IIS环境下WordPress安装部署步骤
下一篇: 简单错误记录