Android - ButterKnife 寫給一知半解的同學(xué)涕侈。

ButterKnife GitHub 2019 年已經(jīng)更新到 10.1.0 版本了沪停,經(jīng)過幾天的學(xué)習(xí)和網(wǎng)上搜索資料,發(fā)現(xiàn)之前版本的一些疑難雜癥已經(jīng)不存在或者有些改善裳涛。剛開始用是非常爽的木张,如果真的要應(yīng)用在項(xiàng)目中,有如下建議:

  1. 一個(gè) module 擼到底的項(xiàng)目端三,直接用吧舷礼,沒啥坑,都挺好郊闯。
  2. 大項(xiàng)目組件化的項(xiàng)目妻献,可以嘗試。低版本據(jù)搜索有很多坑团赁,我在 10.1.0 版本實(shí)驗(yàn)了一下育拨,配置得當(dāng)沒有問題。但我這個(gè)組件化寫得很簡(jiǎn)單欢摄,如果你的項(xiàng)目更加復(fù)雜熬丧,就要?jiǎng)邮衷囋嚵耍枰噧蓚€(gè)地方:一個(gè)看能不能編譯通過剧浸;另一個(gè)看運(yùn)行期間綁定的是否正確锹引。

配置步驟

這個(gè)步驟按照 github 頁(yè)面上的說明設(shè)置就行,這里用列表記錄一下步驟:

給 application 模塊配置:

  1. 設(shè)置為 Java8唆香,介個(gè) 10.1.0 船新版本得用 8 了嫌变。
android {
  ...
  // Butterknife requires Java 8.
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}
  1. 添加依賴:庫(kù) + 注解處理
dependencies {
  implementation 'com.jakewharton:butterknife:10.1.0'
  annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
}

給 library 模塊設(shè)置:

  1. 先按照 application 模塊的方法設(shè)置一遍
  2. 在 project level 的 gradle 文件中添加 buildscript 依賴
buildscript {
  repositories {
    google()
    jcenter()
   }
  dependencies {
    classpath 'com.android.tools.build:gradle:3.4.0'
    classpath 'com.jakewharton:butterknife-gradle-plugin:10.1.0' //  這里
  }
}
  1. 在 library 模塊的 gradle 文件中 apply,寫在 android library 插件的下面
apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife' //  這里

基本使用

舉個(gè)例子

@BindView(R.id.clock_view) //  找到資源
View mClockView;           //  找到變量

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

ButterKnife 的主要目的是簡(jiǎn)化資源和代碼之間的綁定躬它,使用起來(lái)很簡(jiǎn)單腾啥,主要有以下(看起來(lái)像廢話一樣的)幾個(gè)步驟:

  1. 找到資源:R.id.clock_view
    • 如果是 application 模塊,通過 R.id.xxx, R.string.xxx 等找到資源冯吓。
    • 如果是 library 模塊倘待,需要使用 ButterKnife 生成的 R2 來(lái)替代 R。原因如下:

因?yàn)樽⒔庵械?ElementValue 值(寫在括號(hào)內(nèi)的 R.id.xxx)必須是常量组贺,而 R 在 library 中的 id 值都是變量凸舵。

為什么注解中的 ElementValue 必須是常量?因?yàn)槭褂米⒔鈺r(shí)產(chǎn)生的所有信息在編譯期間必須確定下來(lái)失尖,直接寫入注解內(nèi)部的數(shù)據(jù)結(jié)構(gòu)中啊奄。即使是 @Retention(RetentionPolicy.RUNTIME) 修飾的注解渐苏,也不可能在運(yùn)行時(shí)動(dòng)態(tài)運(yùn)行一段字節(jié)碼來(lái)計(jì)算,這樣會(huì)徒增復(fù)雜度而且沒有什么好處菇夸。

在 application 模塊中琼富,R 類中的標(biāo)識(shí)符都是 final 的,也就是常量庄新,是在編譯期就能確定值的鞠眉。而在 library 模塊中,R 類中的標(biāo)識(shí)符不是 final 的择诈,在編譯期無(wú)法確定械蹋。library 中不用常量的理由是 R 中的各種 id 值在一個(gè) app 內(nèi)必須是互不相同的,如果 library 模塊在編譯期就將這些 id 確定為常量的話吭从,那么必須要考慮所有編譯模塊朝蜘,會(huì)降低編譯速度;而且 library 模塊是共享的涩金,如果使用了固定的 id 值谱醇,分發(fā)給其他項(xiàng)目使用難免會(huì)產(chǎn)生沖突。具體分析可以參考這個(gè)官方頁(yè)面:Non-constant Fields in Case Labels

  1. 找到變量:View mClockView;
    • 不能聲明成 private步做,因?yàn)橐ㄟ^ ButterKnife 生成的類訪問副渴,而 private 修飾的成員只有自己才能訪問。ButterKnife 生成的類與綁定的類在同一個(gè)包內(nèi)全度,直接什么都不寫用包訪問權(quán)限就可以了煮剧。
    • 一旦寫好變量和修飾它的注解,ButterKnife 就可以生成綁定的代碼了将鸵,注意只是生成了綁定的代碼勉盅,如果不調(diào)用這個(gè)生成的代碼,也是沒有完成綁定的顶掉。綁定的代碼很簡(jiǎn)單草娜,就是將找到的資源和變量關(guān)聯(lián)起來(lái)。
  2. 綁定:ButterKnife.bind(this);
    • 這個(gè)步驟就是調(diào)用 ButterKnife 生成的綁定代碼痒筒。在調(diào)用綁定代碼之前宰闰,應(yīng)該設(shè)置好 layout 文件,以便能通過資源 id 找到資源簿透;在調(diào)用綁定代碼之后移袍,成員變量才綁定到資源上,這時(shí)才能訪問成員變量老充。

    • 根據(jù)綁定變量所在的類的類型葡盗,bind 方法還有幾個(gè)版本,比如綁定 Fragment 中的變量要用 bind(this, view)啡浊,完整代碼:

      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
          View view = inflater.inflate(R.layout.fragment_first, container, false);
          unbinder = ButterKnife.bind(this, view);
          return view;
      }
      
    • bind 方法的參數(shù) target 必須是綁定的成員所在的類的對(duì)象戳粒。

    • bind 方法會(huì)返回一個(gè) Unbinder 對(duì)象路狮,可以用來(lái)解除綁定。但幾乎所有情況都不需要手動(dòng)調(diào)用蔚约,只有在 Fragment 中綁定才需要在 onDestroyView() 中手動(dòng)解除。

原理簡(jiǎn)要分析

說白了 ButterKnife 就是替我們寫了一些重復(fù)度很高的代碼涂籽,我們只用關(guān)注綁定關(guān)系本身苹祟,重復(fù)的綁定代碼都由 ButterKnife 生成。

那么憑什么 ButterKnife 能替我們寫代碼呢评雌,就要借助注解(Annotation)和注解處理工具(APT)了树枫。

簡(jiǎn)單說一下注解,注解就是帶 @ 符號(hào)開頭的修飾類景东、方法砂轻、變量等等的一些看起來(lái)不像代碼的東西。Java 語(yǔ)言本身有一些內(nèi)置的注解斤吐,最常見的要數(shù) @Override 了搔涝。可以把注解當(dāng)做給 Java 中的類和措、方法庄呈、字段等語(yǔ)法元素添加屬性,再通過各種工具進(jìn)行處理派阱,來(lái)達(dá)到一定的目的诬留。對(duì) ButterKnife 來(lái)說,目的就是將資源和變量關(guān)聯(lián)起來(lái)贫母,不用再手動(dòng)調(diào)用綁定的代碼文兑。ButterKnife 利用注解給成員賦予了資源 id 的屬性,再經(jīng)過 ButterKnife 的處理就可以將兩者關(guān)聯(lián)在一起腺劣。

那么是怎么關(guān)聯(lián)的呢绿贞?這就要提一下注解的兩種主要使用形式:反射和生成代碼。ButterKnife 早期的版本就是用的反射來(lái)實(shí)現(xiàn)綁定誓酒,后來(lái)發(fā)現(xiàn)效率不如生成代碼的實(shí)現(xiàn)方式樟蠕,于是就改成了生成代碼的方式。

生成代碼的過程使用了注解處理工具(APT)靠柑,它是一個(gè)運(yùn)行在構(gòu)建流程中的一個(gè)工具寨辩,可以讀取到注解和被注解元素的信息,再通過自定義的處理器(也就是 gradle 依賴中的annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0')輸出 Java 源代碼歼冰,并將其納入構(gòu)建過程靡狞。

接下來(lái)從代碼角度分析原理:

  1. 對(duì)于綁定的成員所在的類,ButterKnife 會(huì)生成一個(gè)對(duì)應(yīng)的 Unbinder 類型隔嫡,也就是實(shí)現(xiàn)了 Unbinder 接口的一個(gè)類甸怕。
    • 別看它叫 Unbinder甘穿,實(shí)際上綁定和解綁都是使用這個(gè)對(duì)象,綁定用構(gòu)造方法梢杭,解綁用 unbind() 方法温兼。
    • 這個(gè)類與被綁定的類在同一個(gè)包下,因此可以訪問到包訪問權(quán)限的成員武契。
    • 這個(gè)類的名字是按照規(guī)則生成的募判,被綁定類的名字加一個(gè)固定后綴就是這個(gè) Unbinder 類型的名字。
  2. ButterKnife.bind(target, view) 方法內(nèi)部會(huì)根據(jù)傳入的 target 類型咒唆,通過名稱規(guī)則拼接出生成的 Unbinder 類型的類名稱届垫,然后使用反射調(diào)用構(gòu)造方法,也就是執(zhí)行了綁定的代碼全释。再將這個(gè) Unbinder 對(duì)象返回装处,這樣就可以通過這個(gè)對(duì)象調(diào)用 unbind() 方法來(lái)解除綁定。

組件化的影響

先說明一下我這個(gè)簡(jiǎn)單的組件化是怎么做的:

  1. 在 gradle.properties 文件中定義了一個(gè)變量 isModule_libdemo1=true 用來(lái)設(shè)置作為 library 還是作為 application浸船。
  2. 在 library 模塊的 build.grale 中妄迁,通過 isModule_libdemo1 的值來(lái)使用不同 plugin。
if (isModule_libdemo1.toBoolean()) {
    apply plugin: 'com.android.library'
} else {
    apply plugin: 'com.android.application'
}
  1. 使用不同的 manifest 文件(略)

ButterKnife 在組件化過程中有什么坑嗎糟袁?

由于是 library 模塊判族,代碼用的都是 R2 對(duì)象,一旦切換 plugin项戴,由 library 變成 application形帮,就沒有 R2 對(duì)象了嗎,就應(yīng)該用 R 對(duì)象了嗎周叮?

if (isModule_libdemo1.toBoolean()) {
    apply plugin: 'com.android.library'
} else {
    apply plugin: 'com.android.application'
}
apply plugin: 'com.jakewharton.butterknife'

即使是 application辩撑,也可以使用 ButterKnife 插件,可以生成 R2 對(duì)象仿耽,因此應(yīng)該統(tǒng)一使用 R2 來(lái)尋找資源合冀。

其他注意事項(xiàng)

  • 重構(gòu)資源名稱:用重構(gòu)工具可以幫你快速修改。但 ButterKnife 生成的 R2 中的名字 Android Studio 可不管项贺,這時(shí)編譯一下就能發(fā)現(xiàn)問題君躺。但最好不要改成原來(lái)就有的名字,會(huì)沒有任何提示默默編譯成功开缎,在運(yùn)行的時(shí)候給你出錯(cuò)棕叫。
  • @OnClick 與 R2:在 @OnClick(id) 修飾的方法中,即使 id 是 R2 的寫法奕删,方法體內(nèi)部仍要使用 R 對(duì)象中的 id 來(lái)區(qū)分多個(gè)按鈕的 id俺泣。可以這么理解,R2 是 ButterKnife 為了繞開資源 id 非常量的問題伏钠,R2R 是能一一對(duì)應(yīng)的横漏,因此能夠在生成的 Unbinder 代碼使用正確的 R 中的 id。

(ole)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末熟掂,一起剝皮案震驚了整個(gè)濱河市缎浇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌打掘,老刑警劉巖华畏,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尊蚁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)侣夷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門横朋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人百拓,你說我怎么就攤上這事琴锭。” “怎么了衙传?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵决帖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我蓖捶,道長(zhǎng)地回,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任俊鱼,我火速辦了婚禮刻像,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘并闲。我一直安慰自己细睡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布帝火。 她就那樣靜靜地躺著溜徙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪犀填。 梳的紋絲不亂的頭發(fā)上蠢壹,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音宏浩,去河邊找鬼知残。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的求妹。 我是一名探鬼主播乏盐,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼制恍!你這毒婦竟也來(lái)了父能?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤净神,失蹤者是張志新(化名)和其女友劉穎何吝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹃唯,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爱榕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坡慌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黔酥。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖洪橘,靈堂內(nèi)的尸體忽然破棺而出跪者,到底是詐尸還是另有隱情,我是刑警寧澤熄求,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布渣玲,位于F島的核電站,受9級(jí)特大地震影響弟晚,放射性物質(zhì)發(fā)生泄漏忘衍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一指巡、第九天 我趴在偏房一處隱蔽的房頂上張望淑履。 院中可真熱鬧,春花似錦藻雪、人聲如沸秘噪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)指煎。三九已至,卻和暖如春便斥,著一層夾襖步出監(jiān)牢的瞬間至壤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓鲤看,卻偏偏與公主長(zhǎng)得像镰绎,于是被迫代替她去往敵國(guó)和親脓斩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

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