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 库进行依赖管理?的更多相关文章

  1. Android使用gradle依赖管理、依赖冲突终极解决方案(转)

    Android使用gradle依赖管理.依赖冲突终极解决方案在Android开发中,相信遇到关于版本依赖的问题的同学有不少.虽然Android Studio一般都会自动帮我们去重,但是有时候去重失败了 ...

  2. Android 统一配置依赖管理

    Android Studio中默认就是使用Gradle来构建管理工程的,当我们在工程构建过程中创建了多个Module时,就可能存在一个问题,那就是每个Module以及Module中一些公用库的依赖存在 ...

  3. bower解决js库的依赖管理

    从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发.Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎.chrome浏 ...

  4. Android Gradle统一依赖管理

    目的: 避免在依赖包出新版本时,需要对每个module中的build.gradle文件都进行修改(如appcompat-v7包),使用这种方式即只需一次修改. 方法一 在项目的根目录创建一个gradl ...

  5. PHP依赖管理工具Composer入门

    作者: JeremyWei | 可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明 网址: http://weizhifeng.net/manage-php-dependency-wi ...

  6. Android依赖管理与私服搭建

    在Android开发中,一个项目需要依赖许多的库,我们自己写的,第三方的等等,这篇文件介绍的就是自己搭建私服,创建自己的仓库,进行对我们自己写的库依赖管理.本文是在 mac book pro 环境上搭 ...

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

    依赖管理 这会是一个系列,所以如果你还没有看我之前的几篇文章,请先查看以下文章: Gradle for Android 第一篇( 从 Gradle 和 AS 开始 ) Gradle for Andro ...

  8. 【转载】Gradle for Android 第三篇( 依赖管理 )

    依赖管理是Gradle最闪耀的地方,最好的情景是,你仅仅只需添加一行代码在你的build文件,Gradle会自动从远程仓库为你下载相关的jar包,并且保证你能够正确使用它们.Gradle甚至可以为你做 ...

  9. Android Studio配置统一管理依赖版本号引用

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本Demo采用的是其中一个方案,其他方案请阅读参考资料<Android Studio中统一管理版本号引用配置> 使用步骤 ...

随机推荐

  1. Java基础知识强化之IO流笔记28:BufferedOutputStream / BufferedInputStream(字节缓冲区流) 之BufferedOutputStream写出数据

    1. BufferedOutputStream / BufferedInputStream(字节缓冲区流)的概述 通过定义数组的方式确实比以前一次读取一个字节的方式快很多,所以,看来有一个缓冲区还是非 ...

  2. HTML+CSS基础学习笔记(5)

    一.文字排版--字体 Eg: body{font-family:"宋体";} body{font-family:"Microsoft Yahei";}//微软雅 ...

  3. php面向对象设计模式

    为什么学习设计模式: 1,更深入的了解面向对象的思想 2,有利于开发出扩展性强的东西 什么是设计模式:经常出现的典型场景的典型解决方案,就是设计模式.举个例子生活中的设计模式:比如泡妞思路,象棋招数等 ...

  4. AsyncTask理解- Day36or37

    AsyncTask理解- Day36or37 mobile 5.0 1.手机归属地查询 AtoolsActivity Assets目录特点 该文件是原生文件,不会对里面的文件进行编码 该文件只支持读取 ...

  5. sql - union all

    我的 表1中有字段([c],[num]), 记录诸如: [c] [num] 0   188 1   167 2   373 3   378 4   377 表二也有同样的字段,记录有的id不同, 请问 ...

  6. java输入输出流(内容练习)

    1,编写一个程序,读取文件test.txt的内容并在控制台输出.如果源文件不存在,则显示相应的错误信息. package src; import java.io.File; import java.i ...

  7. 删除svn密码方法

    很多时候使用svn,我们需要切换svn账号,但是由于之前的账号已经选择了记住密码,那么我们应该如何删除svn密码来切换新的svn账号呢? 其实很简单,svn账号密码信息保存在电脑某一文件中,我们只要删 ...

  8. 标准的最大margin问题

    standard large margin problem 分割线

  9. boost::xml——基本操作以及中文乱码解决方案 (续)

    本博文主要想说明以下两点: 1.对于上一篇的<boost::xml——基本操作以及中文乱码解决方案>解释,这篇博文基本解决了正确输入输出中英文问题,但是好像还没有解决修改中文出现乱码的问题 ...

  10. Codeforces 549C The Game Of Parity(博弈)

    The Game Of Parity Solution: 这个题只需要分类讨论就可以解决. 先分别统计奇数和偶数的个数. 然后判断谁走最后一步,如果走最后一步时候同时有偶数和奇数,那么走最后一步的赢. ...