如何对 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/

相关文章
相关标签/搜索