如何对 Android 库进行依赖管理?

Android 开发人员为项目选择库的时候,考虑的因素不仅仅是功能、可用性、性能、文档丰富度和技术支持情况。他们还关心库的大小,以及要添加的方法数量。因为项目越大,依赖也越多,要把应用的方法数量控制在65k 以下,开发人员感觉很有压力。另外,对于非发行版项目而言,Proguard 使用起来效率太低,而且开发人员视 multidex 如瘟疫,避之唯恐不及。因此,编写库的作者必须特别注意项目的大小。

为了减少库的方法数量,最简单的途径就是不包含任何多余的依赖。因为你包含的所有依赖,都会被传递并添加至用户的项目里。举个例子,如果你只需要几个简单的工具方法,比如默默地关闭一个资源,那就没必要为此添加 [Guava](https://github.com/google/guava 「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](https://github.com/square/retrofit/tree/version-one「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 官方技术博客

本文转自 OneAPM 官方博客

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

时间: 2024-10-30 06:20:36

如何对 Android 库进行依赖管理?的相关文章

Gradle for Android 第三篇( 依赖管理 )

Gradle for Android 第三篇( 依赖管理 ) 依赖管理是Gradle最闪耀的地方,最好的情景是,你仅仅只需添加一行代码在你的build文件,Gradle会自动从远程仓库为你下载相关的jar包,并且保证你能够正确使用它们.Gradle甚至可以为你做的更多,包括当你在你的工程里添加了多个相同的依赖,gradle会为你排除掉相同的jar包. 作者:佚名来源:Android开发中文站|2017-04-10 17:35  移动端  收藏   分享 依赖管理 依赖管理是Gradle最闪耀的地

Java Gradle入门指南之依赖管理(添加依赖、仓库、版本冲突) (转)

本文为作者原创,转载请注明出处:http://www.cnblogs.com/gzdaijie/p/5296624.html 目录 1.添加依赖包名1.1 依赖类型1.2 声明依赖1.3 添加java依赖1.4 查找依赖包名1.5 完整的例子2.添加依赖仓库3.依赖常见问题3.1 依赖传递性3.2 版本冲突3.3 动态依赖3.4 更多设置 开发任何软件,如何管理依赖是一道绕不过去的坎,软件开发过程中,我们往往会使用这样那样的第三方库,这个时候,一个好的依赖管理就显得尤为重要了.作为一个自动构建工

漫谈依赖管理工具:从Maven,Gradle到Go

为什么要有依赖管理工具? 谈依赖管理之前,我们先谈谈为什么要有依赖管理工具这东西. 我们学了一种编程语言,然后写了个"Hello World",然后宣称自己学了一门语言,这时候确实不需要关心依赖问题. 然而,当你要写一个稍微复杂点的应用,那怕就是留言板这样的,需要读写数据库,就需要依赖数据库驱动,就会遇到依赖管理的问题了. 再进一步,你写了一个库,想共享给别人使用,更需要了解依赖管理的问题. 当然,如果项目足够简单,你可以直接将依赖方的源码放置在自己的项目中,或者将依赖库的二进制文件(

我们也说说Android.mk(4) - 依赖:目标编程的模式

我们也说说Android.mk(4) - 依赖:目标编程的模式 另一种范式 我一直觉得,Makefile确实是C/C++程序员的良配,因为Makefile所使用的两种范式都是C/C++程序员不熟悉的,一种是函数式的思想,一种是依赖构成的目标链的模式. Makefile从最基本上来说,可以抽象成下面这样的: target ... : prerequisites ... command ... ... 如大家所熟悉的,这段的意义是:当prerequisites有更新的时候,执行command命令.如

深入实践Spring Boot1.3.1 Maven依赖管理

1.3.1 Maven依赖管理 使用Maven,通过导入Spring Boot的starter模块,可以将许多程序依赖包自动导入工程中.使用Maven的parent POM,还可以更容易地管理依赖的版本和使用默认的配置,工程中的模块也可以很方便地继承它.例如,使用1.2.1节创建的工程,修改pom.xml文件,使用如代码清单1-1所示的简单Maven配置,基本上就能为一个使用Spring Boot开发框架的Web项目开发提供所需的相关依赖. 代码清单1-1 Spring Boot Web基本依赖

《Groovy官方教程》Grape依赖管理器

原文链接 译文链接 译者:JackWang 1 快速入门 1.1添加一个依赖 Grape是一个内嵌在Groovy里的Jar包依赖管理器.Grape让你可以快速添加maven仓库依赖到你的classpath里,使脚本运行更加简单.最简单的一种用法是只需要在你的脚本里添加一个注解: @Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE') import org.springframework.jd

Cocoa 新的依赖管理工具:Carthage

Cocoa 新的依赖管理工具:Carthage 说到 iOS 的依赖管理工具,大家一定首先想到的是 CocoaPods ,不错,CocoaPods 确实是一个很好依赖管理工具,已然成为了 Cocoa 依赖管理的标准,丰富的支持库.文档等等,CocoaPods 相关的文章有很多,这里就不一一赘述了. 今天要介绍的是一款新的依赖管理工具,名曰 "Carthage",名字有点难理解,简单方便,完全独立,不修改 XCode 项目文件或配置. 简介 我之前很少用 CocoaPods,很大一部分原

为什么要开发Android库?

本文讲的是为什么要开发Android库,不论是你要执行一个特定的任务,模块化你的代码,或者只是为了更优雅地重用你的代码,有些时候,作为开发者,通常会考虑开发库来实现.但开发库是个挺困难的事情.由 Bay Android Dev Group 主办,这次分享由我们的 Emanuele Zattin 分享一些他在开发 Java 和 C/C++ 库上的一些最佳实践.探讨下 API 设计,CI 技术,以及对于性能的看法,你会了解到对你工作很有帮助的一些工具. 为什么要开发 Android 库? 为了追求更

检查程序对动态库的依赖

转载请注明出处:http://blog.csdn.net/horkychen 测试程序时会出现找不到指定的库或库函数的错误,表示某个依赖库不存在或版本不正确.如果使用的是动态加载的动态库所依赖的包不存在,如果程序没有做好异常处理的话,会让你很难定位到问题.比如GDI+库.假设一个程序A,调用了动态库B,而B链接了GDI+,这时运行在Windows 2000上时,系统里没有GDI+的库,会导致A不断Crash掉.因为GDI+库不是一般C的动态库,一般没有库加载的动作,使得排查问题会有很多的障碍.