如何对 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/
如何对 Android 库进行依赖管理?的更多相关文章
- Android使用gradle依赖管理、依赖冲突终极解决方案(转)
Android使用gradle依赖管理.依赖冲突终极解决方案在Android开发中,相信遇到关于版本依赖的问题的同学有不少.虽然Android Studio一般都会自动帮我们去重,但是有时候去重失败了 ...
- Android 统一配置依赖管理
Android Studio中默认就是使用Gradle来构建管理工程的,当我们在工程构建过程中创建了多个Module时,就可能存在一个问题,那就是每个Module以及Module中一些公用库的依赖存在 ...
- bower解决js库的依赖管理
从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发.Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎.chrome浏 ...
- Android Gradle统一依赖管理
目的: 避免在依赖包出新版本时,需要对每个module中的build.gradle文件都进行修改(如appcompat-v7包),使用这种方式即只需一次修改. 方法一 在项目的根目录创建一个gradl ...
- PHP依赖管理工具Composer入门
作者: JeremyWei | 可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明 网址: http://weizhifeng.net/manage-php-dependency-wi ...
- Android依赖管理与私服搭建
在Android开发中,一个项目需要依赖许多的库,我们自己写的,第三方的等等,这篇文件介绍的就是自己搭建私服,创建自己的仓库,进行对我们自己写的库依赖管理.本文是在 mac book pro 环境上搭 ...
- Gradle for Android 第三篇( 依赖管理 )
依赖管理 这会是一个系列,所以如果你还没有看我之前的几篇文章,请先查看以下文章: Gradle for Android 第一篇( 从 Gradle 和 AS 开始 ) Gradle for Andro ...
- 【转载】Gradle for Android 第三篇( 依赖管理 )
依赖管理是Gradle最闪耀的地方,最好的情景是,你仅仅只需添加一行代码在你的build文件,Gradle会自动从远程仓库为你下载相关的jar包,并且保证你能够正确使用它们.Gradle甚至可以为你做 ...
- Android Studio配置统一管理依赖版本号引用
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本Demo采用的是其中一个方案,其他方案请阅读参考资料<Android Studio中统一管理版本号引用配置> 使用步骤 ...
随机推荐
- java 数组注意点
int[] array1 = new int[4]; int[] array2 = new int[6]; int[] array3 = new int[8]; array2 = array1; ar ...
- ListView simpleAdapter的基本使用
使用simpleAdapter的数据用一般都是HashMap构成的List,list的每一节对应ListView的每一行.HashMap的每个键 值数据映射到布局文件中对应id的组件上.因为系统没有对 ...
- PHP中用PDO方法打开连接关闭mysql数据库
代码如下: <meta http-equiv="content-type" content="text/html" charset="utf-8 ...
- Bash中的数组
变量:$VAR或者${VAR} 数组:${VAR[$i]} 打印整个数组:echo ${VAR[@]} 统计数组元素个数:echo ${#VAR[@]} 从文件读入数组(按行读入):VAR=(`cat ...
- Google C++ Style Guide在C++11普及后的变化
转 http://www.cnblogs.com/chen3feng/p/5972967.html?from=timeline&isappinstalled=0&lwfrom=user ...
- Andriod中WebView加载登录界面获取Cookie信息并同步保存,使第二次不用登录也可查看个人信息。
Android使用WebView加载登录的html界面,则通过登录成功获取Cookie并同步,可以是下一次不用登录也可以查看到个人信息,注:如果初始化加载登录,可通过缓存Cookie信息来验证是否要加 ...
- AES加解密【示例】
代码 /** * AES算法加密.JRE默认只能用16个字节(128)位密钥 */ public class AESUtils { //使用指定转换的 Cipher 对象 publ ...
- Bootstrap-全局css样式之按钮
这里所说的按钮只是Bootstrap设计的能使标签或元素呈现按钮样式的属性,所以为 <a>.<button> 或 <input> 元素添加按钮类(button cl ...
- SQL SERVER while循环
在SQL数据库中,可以通过WHILE实现循环,下面就将为您介绍SQL循环执行while控制,希望对您提升WHILE的使用水平能够有些帮助. WHILE Boolean_expression { ...
- c++相关知识回顾
1.typedef typedef用来定义同类型的同义词.如: typedef unsingned int size_t; typedef int ptrdiff_t; typedef T * ite ...