butterknife源碼分析

butterknife是一個(gè)Android View和Callback注入框架伤为,相信很多人都在使用啊送,可以減少很多代碼量隅茎,并且避免遺漏綁定而產(chǎn)生的異常鞠眉。

1. 介紹

1.1 優(yōu)點(diǎn)

  • 通過注釋@BindView來消除findViewById
  • 通過注釋@OnClick或者其他來消除綁定事件
  • 通過資源注釋消除資源查找邑商。
    一句話概括,用自動(dòng)生成的代碼來幫助處理以上事情凡蚜,減少開發(fā)者的代碼量人断。

2. 基本用例

因?yàn)楸容^簡(jiǎn)單,可以直接參考butterknife官方介紹朝蜘。下面就以最常用的View綁定和點(diǎn)擊事件綁定為例恶迈。

2.1 使用@BindView綁定View

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.tvResult) TextView tvResult;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        tvResult.setText("Test");
    }
}

2.2 使用@OnClick綁定點(diǎn)擊事件

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }
    @OnClick(R.id.btnFinish)
    void btnFinishClick() {
        finish();
    }
}

3. 源碼分析

源碼結(jié)構(gòu).png

從上圖我們可以看到,butterknife的源碼主要由2部分組成谱醇,一部分是編譯期自動(dòng)生成代碼暇仲,另一部分是通過自動(dòng)生成的代碼對(duì)View或者點(diǎn)擊事件進(jìn)行綁定。

3.2 自動(dòng)代碼生成

代碼自動(dòng)生成部分.png

我們可以看到副渴,每一個(gè)Activity(或者Fragment等)奈附,生成2個(gè)類,分別是"ClassName_ViewBinder"和“ClassName_ViewBinding”煮剧。

生成代碼

主要用了代碼生成框架auto斥滤,以后會(huì)專門結(jié)合ButterKnife寫一篇文章,本文點(diǎn)到為止勉盅,你只要知道解析注解佑颇,通過Auto框架生成了以上的Java文件,不做具體展開草娜。
主要邏輯在ButterKnifeProcessor里的process接口

@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
    //尋找并且生成BindingClass
    Map<TypeElement, BindingClass> targetClassMap = findAndParseTargets(env);

    for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {
      TypeElement typeElement = entry.getKey();
      BindingClass bindingClass = entry.getValue();
      //依次生成Java文件
      for (JavaFile javaFile : bindingClass.brewJava()) {
        try {
          javaFile.writeTo(filer);
        } catch (IOException e) {
          error(typeElement, "Unable to write view binder for type %s: %s", typeElement,
              e.getMessage());
        }
      }
    }

    return true;
  }

3.3 View綁定

綁定View流程圖.png

通過ButterKnife進(jìn)行關(guān)聯(lián)

ButterKnife.bind(this);
  public static Unbinder bind(@NonNull Activity target) {
    return getViewBinder(target).bind(Finder.ACTIVITY, target, target);
  }

根據(jù)Activity獲取ViewBinder

  static ViewBinder<Object> getViewBinder(@NonNull Object target) {
    Class<?> targetClass = target.getClass();
    if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());
    return findViewBinderForClass(targetClass);
  }

根據(jù)class名稱獲取ViewBinder

  private static ViewBinder<Object> findViewBinderForClass(Class<?> cls) {
    //根據(jù)cls獲取緩存的ViewBinder
    ViewBinder<Object> viewBinder = BINDERS.get(cls);
    //如果緩存存在挑胸,則直接返回使用
    if (viewBinder != null) {
      if (debug) Log.d(TAG, "HIT: Cached in view binder map.");
      return viewBinder;
    }
    String clsName = cls.getName();
    //如果類名是以android或者java開頭,則認(rèn)為是框架類宰闰,不作處理
    if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
      if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
      return NOP_VIEW_BINDER;
    }
    //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
    try {
      //根據(jù)clsName + "_ViewBinder" 實(shí)例化
      Class<?> viewBindingClass = Class.forName(clsName + "_ViewBinder");
      //noinspection unchecked
      viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance();
      if (debug) Log.d(TAG, "HIT: Loaded view binder class.");
    } catch (ClassNotFoundException e) {
      if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
      //如果沒有找到該類茬贵,則實(shí)例化父類
      viewBinder = findViewBinderForClass(cls.getSuperclass());
    } catch (InstantiationException e) {
      throw new RuntimeException("Unable to create view binder for " + clsName, e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Unable to create view binder for " + clsName, e);
    }
    //將實(shí)例化的viewBinder進(jìn)行緩存
    BINDERS.put(cls, viewBinder);
    return viewBinder;
  }

調(diào)用MainActivity_ViewBinder的bind

public final class MainActivity_ViewBinder implements ViewBinder<MainActivity> {
  @Override
  public Unbinder bind(Finder finder, MainActivity target, Object source) {
    return new MainActivity_ViewBinding<>(target, finder, source);
  }
}

實(shí)例化ClassName_ViewBinding類

public class MainActivity_ViewBinding<T extends MainActivity> implements Unbinder {
  protected T target;
  private View view2131492946;
  public MainActivity_ViewBinding(final T target, Finder finder, Object source) {
    this.target = target;
    View view;
    //查找View,并賦值給Activity的tvResult
    target.tvResult = finder.findRequiredViewAsType(source, R.id.tvResult, "field 'tvResult'", TextView.class);
    ...省略代碼
  }
}

查找View

public final <T> T findRequiredViewAsType(Object source, @IdRes int id, String who,
      Class<T> cls) {
    //查找View
    View view = findRequiredView(source, id, who);
    return castView(view, id, who, cls);
  }

   public final View findRequiredView(Object source, @IdRes int id, String who) {
   //查找類
    View view = findOptionalView(source, id);
    if (view != null) {
      return view;
    }
    ...省略代碼
  }

  ACTIVITY {
    @Override public View findOptionalView(Object source, @IdRes int id) {
      //通過Activity查找View
      return ((Activity) source).findViewById(id);
    }
      ...省略代碼
  }

至此完成了View的綁定流程移袍,點(diǎn)擊事件和資源文件綁定流程大同小異解藻,我就不一一介紹了,感興趣的可以自己看下咐容。

6. 參考資料

butterknife
butterknife官方介紹
auto

可以隨意轉(zhuǎn)發(fā)舆逃,也歡迎關(guān)注我的簡(jiǎn)書蚂维,我會(huì)堅(jiān)持給大家?guī)矸窒怼?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戳粒,一起剝皮案震驚了整個(gè)濱河市路狮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔚约,老刑警劉巖奄妨,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異苹祟,居然都是意外死亡砸抛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門树枫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來直焙,“玉大人,你說我怎么就攤上這事砂轻”际模” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵搔涝,是天一觀的道長(zhǎng)厨喂。 經(jīng)常有香客問我,道長(zhǎng)庄呈,這世上最難降的妖魔是什么蜕煌? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮诬留,結(jié)果婚禮上斜纪,老公的妹妹穿的比我還像新娘。我一直安慰自己文兑,他們只是感情好傀广,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著彩届,像睡著了一般伪冰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上樟蠕,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天贮聂,我揣著相機(jī)與錄音,去河邊找鬼寨辩。 笑死吓懈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的靡狞。 我是一名探鬼主播耻警,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了甘穿?” 一聲冷哼從身側(cè)響起腮恩,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎温兼,沒想到半個(gè)月后秸滴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡募判,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年荡含,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片届垫。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡释液,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出装处,到底是詐尸還是另有隱情均澳,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布符衔,位于F島的核電站找前,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏判族。R本人自食惡果不足惜躺盛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望形帮。 院中可真熱鬧槽惫,春花似錦、人聲如沸辩撑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽合冀。三九已至各薇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間君躺,已是汗流浹背峭判。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棕叫,地道東北人林螃。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像俺泣,于是被迫代替她去往敵國(guó)和親疗认。 傳聞我的和親對(duì)象是個(gè)殘疾皇子完残,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 博文出處:ButterKnife源碼分析,歡迎大家關(guān)注我的博客横漏,謝謝谨设! 0x01 前言 在程序開發(fā)的過程中,總會(huì)有...
    俞其榮閱讀 2,021評(píng)論 1 18
  • 主目錄見:Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)?前面我們已經(jīng)講完[編譯期注解的使用例子]大家應(yīng)該對(duì)這個(gè)流程...
    ZJ_Rocky閱讀 1,476評(píng)論 0 8
  • butterknife注解框架相信很多同學(xué)都在用绊茧,但是你真的了解它的實(shí)現(xiàn)原理嗎铝宵?那么今天讓我們來看看它到底是怎么實(shí)...
    打不死的小強(qiáng)qz閱讀 641評(píng)論 0 4
  • 主目錄見:Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)?昨天我們已經(jīng)分析完butterknife的注解處理器的收集...
    ZJ_Rocky閱讀 540評(píng)論 0 3
  • 【天津養(yǎng)生營(yíng)第5天 日精進(jìn)打卡329天】 曾小杰 复蚓颍慧燈教育集團(tuán) 11.16 定位就是命华畏! 【我發(fā)愿】我發(fā)愿...
    曾小杰愛碼字閱讀 278評(píng)論 0 1