ButterKnife閱讀筆記

簡述

在Android開發(fā)中伟骨,很多時候可能都要寫一些模板代碼,比方說最常見的findViewById栈戳,而ButterKnife就是用于降低手寫的工作量的华糖。
通過編譯期的注解方式,生成新的文件瓢棒,然后在使用的時候從該文件中自動調(diào)用findViewById來實現(xiàn)手動的效果浴韭。
這樣的做法有好處當然也有壞處,壞處可能就是會增加方法數(shù)脯宿、使用的時候用到了反射構(gòu)建預先寫好的類念颈、降低編譯的速度,好處簡單說就是降低手動的工作量连霉,至于要不要使用榴芳,這個就看情況定了。
下面講一下ButterKnife的基本實現(xiàn)流程跺撼,因為篇幅有限窟感,只講findViewByID類型

BindView

用于綁定view的id的注解

@Retention(CLASS)//編譯期注解,不會保留到運行時
@Target(FIELD)//作用對象為參數(shù)
public @interface BindView {
  //當前綁定對象的id
  @IdRes int value();
}

這個其實就是說明了通過編譯期的注解歉井,預先定義好了一個視圖對象和id的關聯(lián)關系

    @BindView(R.id.tv_text)
    TextView tvText;

這個實際上就是說明tvText的id是R.id.tv_text肌括,而R.id.tv_text實際上就是一個int值,這個可以在R文件中看到

編譯期處理

因為代碼有點多,這里不細講代碼谍夭,主要講述一下流程:
1.通過實現(xiàn)AbstractProcessor來進行編譯期對于注解的處理
2.獲得當前ButterKnife所有支持的注解元素
3.檢查注解的作用域是否合理,通過該注解獲得當前參數(shù)憨募、注解的id等屬性
4.將每一個滿足條件的注解對象放入集合當中
5.為每一個有ButterKnife注解的類都重新寫一個java文件紧索,其中里面通過之前收集的集合中的數(shù)據(jù)進行寫入,主要是默認寫入class.view(注解的參數(shù)對象) = targetView.findViewById(注解的id值)菜谣,那么在后期進行bind的時候調(diào)用該新寫的類即可實現(xiàn)自動的效果

ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding");

這里可以看到實際上生成的輔助類就是在當前有ButterKnife注解的類_ViewBinding珠漂,比方說當前為AActivity,那么自動生成的類就是AActivity_ViewBinding

運行時處理

以一個例子說明尾膊,假設當前處理的類為MainActivity媳危,其中有一個

@BindView(R.id.tv_text)
TextView tvText;

在運行時,說明編譯已經(jīng)完成冈敛,這個時候輔助類已經(jīng)生成待笑,接下來要做的就是調(diào)用該輔助類

ButterKnife.bind(this);

通過默認提供的bind方法即可實現(xiàn)這個效果,具體看一下實現(xiàn)細節(jié)

  public static Unbinder bind(@NonNull Activity target) {
    //獲取當前Activity對應的Window的DecorView
    View sourceView = target.getWindow().getDecorView();
    return createBinding(target, sourceView);
  }

  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());
    Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);

    if (constructor == null) {
      return Unbinder.EMPTY;
    }

    //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
    try {
      //這里僅僅是調(diào)用構(gòu)造函數(shù)生成新的Unbinder對象
      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);
    }
  }

可以看到這里是通過反射的方法創(chuàng)建了一個對象抓谴,當然到這里可以猜到其實就是反射構(gòu)建以前編譯期生成的輔助類暮蹂。

  private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
    //嘗試從緩存中獲取對應的構(gòu)造函數(shù),這里只有解析過某一個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.")) {//過濾Java和Android的內(nèi)部類
      if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
      return null;
    }
    try {
      //通過ClassLoader讀取名稱為MainActivity_ViewBinding的類(一個例子)
      //其中這個文件是在編譯期間的ButterKnifeProcessor中生成的
      Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
      //noinspection unchecked
      //獲取指定的構(gòu)造函數(shù)癌压,其中有兩個Class參數(shù)(target,view)
      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);
    }
    //放入緩存當中,再次拉起當前頁面的時候滩届,可以直接從緩存中獲取構(gòu)造函數(shù)集侯,從而節(jié)省反射的開支
    BINDINGS.put(cls, bindingCtor);
    return bindingCtor;
  }

這里有一些優(yōu)化手段先不提,主要是看到通過ClassLoader來加載clsName + "_ViewBinding"的類帜消,這里其實就是MainActivity_ViewBinding棠枉,這個就是編譯期生成的類。
然后最終在MainActivity_ViewBinding(Activity a, View v)這個方法中完成了預定義的一系列findViewById操作

插件

如果說已經(jīng)決定使用ButterKnife的話券犁,那么添加頁面化插件的話在使用上就更加方便术健,它可以自動生成注解、id和view的代碼

實例.png

然后在對應的Activity代碼中粘衬,alt+insert(我用的快捷鍵荞估,這個其實就是AS導航欄上面Code里面的Generate)中打開選項,里面就有ButterKnife的選項稚新,后面就簡單了勘伺。

總結(jié)

從使用的層面來說,ButterKnife確實比較方便褂删,特別是有的頁面view特別多的情況下飞醉,不過從代碼的角度上面來說,如果更加考慮編譯的速度和方法數(shù)的話,也許自己封裝findViewByID是一種更加合適的方案缅帘,這個就根據(jù)自己的需求來決定轴术。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钦无,隨后出現(xiàn)的幾起案子逗栽,更是在濱河造成了極大的恐慌,老刑警劉巖失暂,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彼宠,死亡現(xiàn)場離奇詭異,居然都是意外死亡弟塞,警方通過查閱死者的電腦和手機凭峡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來决记,“玉大人,你說我怎么就攤上這事霉涨“醇郏” “怎么了笙瑟?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長往枷。 經(jīng)常有香客問我框产,道長,這世上最難降的妖魔是什么错洁? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮屯碴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘导而。我一直安慰自己忱叭,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布今艺。 她就那樣靜靜地躺著韵丑,像睡著了一般虚缎。 火紅的嫁衣襯著肌膚如雪撵彻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天轴合,我揣著相機與錄音,去河邊找鬼值桩。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的携栋。 我是一名探鬼主播搭盾,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼婉支,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蝌以?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤跟畅,失蹤者是張志新(化名)和其女友劉穎溶推,沒想到半個月后徊件,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蒜危,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年部翘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片新思。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡晃酒,死狀恐怖表牢,靈堂內(nèi)的尸體忽然破棺而出贝次,到底是詐尸還是另有隱情崔兴,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布位谋,位于F島的核電站,受9級特大地震影響掏父,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赊淑,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仅讽。 院中可真熱鬧陶缺,春花似錦、人聲如沸洁灵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至双抽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荠诬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工柑贞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钧嘶。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像有决,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子书幕,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,077評論 25 707
  • 俗話說的好“不想偷懶的程序員挣跋,不是好程序員”顾稀,我們在日常開發(fā)android的過程中,在前端activity或者fr...
    蛋西閱讀 4,966評論 0 14
  • 什么是注解注解分類注解作用分類 元注解 Java內(nèi)置注解 自定義注解自定義注解實現(xiàn)及使用編譯時注解注解處理器注解處...
    Mr槑閱讀 1,075評論 0 3
  • 低頭族啊低頭族, 快來看看藍天白云飄严衬,太陽開口笑 低頭族啊低頭族, 快來看看大海魚兒游请琳,船兒樂悠悠 低頭族啊低頭族...
    沐木醬閱讀 624評論 0 1
  • 今天,小黑兔不想去跑步了俄精,小白兔把他帶到操場,讓他看一群螞蟻辛勤的勞動嘀倒。小黑兔心想:這是要我別偷懶局冰,堅持下去的意思...
    jederik閱讀 135評論 0 0