ButterKnife的基本使用和實現原理

一.基本使用
1.在Project的build.gradle中添加如下配置

buildscript {
  repositories {
    mavenCentral()
   }
  dependencies {
 	 //添加插件
    classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1'
  }
}

2.在Module的build.gradle添加如下配置

//引入插件
apply plugin: 'com.jakewharton.butterknife'

android {
  ...
}

dependencies {
		//導入
 		compile 'com.jakewharton:butterknife:8.8.1'
		annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}

使用的相關注解
@BindView—->綁定一個view;id爲一個view 變量
@BindViews —-> 綁定多個view;id爲一個view的list變量
@BindArray—-> 綁定string裏面array數組;@BindArray(R.array.city ) String[] citys ;
@BindBitmap—->綁定圖片資源爲Bitmap;@BindBitmap( R.mipmap.wifi ) Bitmap bitmap;
@BindBool —->綁定boolean值
@BindColor —->綁定color;@BindColor(R.color.colorAccent) int black;
@BindDimen —->綁定Dimen;@BindDimen(R.dimen.borth_width) int mBorderWidth;
@BindDrawable —-> 綁定Drawable;@BindDrawable(R.drawable.test_pic) Drawable mTestPic;
@BindFloat —->綁定float
@BindInt —->綁定int
@BindString —->綁定一個String id爲一個String變量;@BindString( R.string.app_name ) String meg;
@OnClick—->點擊事件
@OnCheckedChanged —->選中,取消選中
@OnLongClick —->長按事件
@OnPageChange —->頁面改變事件
@OnTextChanged —->EditText裏面的文本變化事件
@OnTouch —->觸摸事件
等等

在Activity中的使用

public class MainActivity extends AppCompatActivity {
    //相當於findViewById
    @BindView(R.id.tv_show)
    TextView tvShow;

    //加載資源文件
    @BindDrawable(R.drawable.ic_launcher_background)
    Drawable	icon;

    //點擊事件
    @OnClick(R.id.btn_go)
    public void go(){

    }

    Unbinder unbinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //綁定
        unbinder = ButterKnife.bind(this);

        tvShow.setText("綁定成功");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解綁
        if (unbinder != null) {
            unbinder.unbind();
        }
    }
}

更多詳情查看官方說明https://github.com/JakeWharton/butterknife
二.源碼分析
從綁定ButterKnife.bind(this)源碼開始

public static Unbinder bind(@NonNull Activity target) {
    View sourceView = target.getWindow().getDecorView();
    return createBinding(target, sourceView);
  }

ButterKnife中createBinding(target, sourceView)方法

/**
*	target  activity
*	source decorView
*/
 private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
    Class<?> targetClass = target.getClass();
    //返回Unbinder的實現類的構造函數 這裏即MainActivity_ViewBinding的構造方法
    Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);

    if (constructor == null) {
      return Unbinder.EMPTY;
    }
    try {
    //相當於創建了MainActivity_ViewBinding對象
      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);
    }
  }

ButterKnife的findBindingConstructorForClass()方法

//存儲着bind類對應的Unbinder對象
static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>();
//cls	activity對應的class
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
	//從緩存中獲取Unbinder的構造方法
    Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
    if (bindingCtor != null) {
      return bindingCtor;
    }
    String clsName = cls.getName();
    //排除android和java包下的
    if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
      return null;
    }
    try {
    	//clsName + "_ViewBinding" 相當於 MainActivity_ViewBinding 實現了implements Unbinder 
      Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
      //獲取MainActivity_ViewBinding的構造方法		public MainActivity_ViewBinding(final MainActivity target, View source)
      bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
      
    } catch (ClassNotFoundException e) {
      bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
    } catch (NoSuchMethodException e) {
      throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
    }
    //將對應綁定類和Unbinder對象映射存儲起來
    BINDINGS.put(cls, bindingCtor);
    //返回Unbinder的實現類
    return bindingCtor;
  }

Unbinder的實現類MainActivity_ViewBinding 的源碼如下

public class MainActivity_ViewBinding implements Unbinder {
//這裏擁有MainActivity的引用
  private MainActivity target;
  private View view2131165218;

  @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.tvShow = Utils.findRequiredViewAsType(source, R.id.tv_show, "field 'tvShow'", TextView.class);
    view = Utils.findRequiredView(source, R.id.btn_go, "method 'go'");
    view2131165218 = view;
    //設置點擊事件
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.go();
      }
    });

    Context context = source.getContext();
    target.icon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background);
  }

  @Override
  @CallSuper
  public void unbind() {
    MainActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    //將綁定的數據清空
    this.target = null;
    target.tvShow = null;
    view2131165218.setOnClickListener(null);
    view2131165218 = null;
  }
 public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
      Class<T> cls) {
    View view = findRequiredView(source, id, who);
    return castView(view, id, who, cls);
  }
  public static View findRequiredView(View source, @IdRes int id, String who) {
  //實際調用了findViewById這句代碼
    View view = source.findViewById(id);
    if (view != null) {
      return view;
    }
    ..........................................
  }

那麼我們並沒有MainActivity_ViewBinding 這個類,而MainActivity_ViewBinding 這個類是通過APT在編譯時給我們生成的

APT(Annotation Processing Tool)是一種處理註釋的工具,它對源代碼文件進行檢測找出其中的Annotation,使用Annotation進行額外的處理。 Annotation處理器在處理Annotation時可以根據源文件中的Annotation生成額外的java源文件和其它的文件
(文件具體內容由Annotation處理器的編寫者決定),APT還會編譯生成的源文件和原來的源文件,將它們一起生成class文件。
關於APT生成ViewBinding類相關源碼參考https://www.jianshu.com/p/0f3f4f7ca505

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章