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

jopen 8年前發布 | 22K 次閱讀 安卓開發 Android開發 移動開發

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

為了減少庫的方法數量,最簡單的途徑就是不包含任何多余的依賴。因為你包含的所有依賴,都會被傳遞并添加至用戶的項目里。舉個例子,如果你只需要幾個簡單的工具方法,比如默默地關閉一個資源,那就沒必要為此添加 Guava 。自己編寫方法,或者從一個現有的庫中提取(但是務必做出說明)就可以了。用戶肯定會感激你去除了多余的14k方法。

但是,這并不是說你不該使用外部庫,而是你要做出明智的選擇。比如,像 HTTP 客戶端這樣的庫已經有了,你若再去重寫一個,最終結果只能是浪費時間,倒不如用這些時間改進自己的庫。

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

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

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

在此筆者要提醒大家,如果你發現自己庫中的某些功能只對少數用戶有用,你應該認真考慮一下是否還要保留這些功能。關于這一點,后文中還會講到。

在庫里包含可選擇性依賴的另一個原因,是Android 框架已經提供了一種解決方案,但是某個外部庫提供的解決方案性能更好。如果用戶本就依賴于該外部庫,或者愿意增加方法數量以獲得更好的性能,就可以添加可選擇性依賴。

我最近看到的 PlacesAutocompleteTextView 庫,就屬于這種情況。該庫使用的內部 HTTP 客戶端,既可以是 OkHttpClient,也可以是 HttpURLConnection。通常,前者的性能更好,但是需要添加 OkHttp 作為依賴。 如果用戶不想包含該依賴,可以自動從標準庫回退到 HttpURLConnection。

為此,需要一個「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 官方技術博客。

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

來自: http://news.oneapm.com/android_developers/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!