如何對 Android 庫進行依賴管理?

Android 開發人員爲項目選擇庫的時候,考慮的因素不只僅是功能、可用性、性能、文檔豐富度和技術支持狀況。他們還關心庫的大小,以及要添加的方法數量。由於項目越大,依賴也越多,要把應用的方法數量控制在65k 如下,開發人員感受頗有壓力。另外,對於非發行版項目而言,Proguard 使用起來效率過低,並且開發人員視 multidex 如瘟疫,避之惟恐不及。所以,編寫庫的做者必須特別注意項目的大小。html

爲了減小庫的方法數量,最簡單的途徑就是不包含任何多餘的依賴。由於你包含的全部依賴,都會被傳遞並添加至用戶的項目裏。舉個例子,若是你只須要幾個簡單的工具方法,好比默默地關閉一個資源,那就不必爲此添加 Guava。本身編寫方法,或者從一個現有的庫中提取(可是務必作出說明)就能夠了。用戶確定會感激你去除了多餘的14k方法。android

可是,這並非說你不應使用外部庫,而是你要作出明智的選擇。好比,像 HTTP 客戶端這樣的庫已經有了,你若再去重寫一個,最終結果只能是浪費時間,倒不如用這些時間改進本身的庫。git

除了有選擇地使用庫之外,還有幾個策略也能夠幫助你保持庫的精簡。其中一個策略就是使用 provided scope(已提供範圍)來聲明依賴。 這是 gradle 中 Android build system(Android 構建系統)的一部分。與 compile scope(編譯範圍)不一樣,provided scope 僅在編譯時包含依賴。這就意味着,用戶在構建項目時,該依賴不會隨着 APK 文件打包。若是想在運行時使用該依賴,用戶須要在應用的 build.gradle 裏顯式聲明。github

注意: 還有一種與 provided scope 相反的機制叫 package scope(包範圍)。這種依賴會隨 APK 文件打包,可是在編譯時不可用。性能優化

你可能還想在庫中使用可選擇性依賴。其中一個緣由是,某些功能可能只有部分用戶使用。例如 Retrofit 1.x,該庫可使用 REST 調用來響應,而不使用回調。那些想使用 RxJava 的用戶能夠添加之,而不想使用它的用戶也能夠不添加,以避免加劇負擔。自從 Retrofit 使用 maven build system(maven 構建系統)之後,其配置稍有更改,但理念仍是類似的。網絡

在此筆者要提醒你們,若是你發現本身庫中的某些功能只對少數用戶有用,你應該認真考慮一下是否還要保留這些功能。關於這一點,後文中還會講到。框架

在庫裏包含可選擇性依賴的另外一個緣由,是Android 框架已經提供了一種解決方案,可是某個外部庫提供的解決方案性能更好。若是用戶本就依賴於該外部庫,或者願意增長方法數量以得到更好的性能,就能夠添加可選擇性依賴。maven

我最近看到的PlacesAutocompleteTextView庫,就屬於這種狀況。該庫使用的內部 HTTP 客戶端,既能夠是 OkHttpClient,也能夠是 HttpURLConnection。一般,前者的性能更好,可是須要添加 OkHttp 做爲依賴。 若是用戶不想包含該依賴,能夠自動從標準庫回退到 HttpURLConnection。ide

爲此,須要一個「resolver」 類以肯定運行時要使用的依賴。 例如,如下的類就用於選擇 HTTP 客戶端:工具

public final class PlacesHttpClientResolver {
  public static final PlacesHttpClient PLACES_HTTP_CLIENT;

  static {
    boolean hasOkHttp;

    try {
      Class.forName("com.squareup.okhttp.OkHttpClient");
      hasOkHttp = true;
    } catch (ClassNotFoundException e) {
      hasOkHttp = false;
    }

    PlacesApiJsonParser parser = JsonParserResolver.JSON_PARSER;

    PLACES_HTTP_CLIENT = hasOkHttp ? new OkHttpPlacesHttpClient(parser) : new HttpUrlConnectionMapsHttpClient(parser);
  }

  private PlacesHttpClientResolver() {
    throw new RuntimeException("No Instances!");
  }
}

該類被加載時,會檢查 OkHttpClient 的徹底限定類名是否可用。若是拋出 ClassNotFoundException,咱們就知道用戶沒有添加 OkHttp,因而回退到 HttpURLConnection。PlacesHttpClient 是包裝以上兩種實現方式的公共接口,所以在整個代碼庫中,這兩種實現方式能夠交換使用。JSON 解析也採用了一樣的方法,Gson 可選擇性地做爲依賴包含在庫中。

若是性能表現與庫的大小之間的權衡係數很大,這個方法確實不錯。可是,若是回退的實現方式比較困難(好比 JSON 解析就是這種狀況),筆者建議你先使用外部庫來節省時間,在後續的版本中再考慮添加回退實現。

筆者在前文中提到,你應該對庫中包含的功能作出明智的選擇。若是某個功能幾乎全部用戶都不須要,最好將其除去,並且這裏也沒有必要使用前面提到的第一種可選擇性依賴。再次以 Retrofit 爲例,在 2.x 版本 中,使用 REST 調用來響應這個功能,再也不做爲核心庫的一部分提供給用戶,而是移到一個單獨的模塊上,並做爲 Retrofit 的 maven 構件發佈 。

一樣地,不一樣的響應轉換器也被拆成了獨立的依賴。例如,Retrofit 用戶想要轉換一個 JSON 響應,並且已經依賴於 Gson,他們能夠在 build.gradle 文件中添加如下依賴:

dependencies {
  compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
}

而那些使用其餘 JSON 庫(好比 Jackson)的用戶,或者須要解析其餘數據格式(好比 XML 或 protocol buffers)的用戶,也能夠採用這種方式添加本身須要的依賴,並且避免通用型依賴帶來的額外負擔。與此同時,核心庫也不會被這些附加功能干擾,能夠專一於須要解決的主要問題。

總而言之,若是你正在編寫的庫有意給 Android 開發人員使用,在設計時務必記住以上幾個策略。庫的大小,不該該只當作屬性,而應該視爲一種特性予以考慮,你的用戶絕對會所以而感激你!

OneAPM Mobile Insight以真實用戶體驗爲度量標準進行 Crash 分析,監控網絡請求及網絡錯誤,提高用戶留存。訪問 OneAPM 官方網站感覺更多應用性能優化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術博客

本文轉自 OneAPM 官方博客

原文地址:http://johnpetitto.com/android-lib-dependency-management/

相關文章
相關標籤/搜索