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数组 数组工具类Arrays

    一.数组 1.java有严格的数据类型限制,一个数组只能声明一个数据类型,存放同一种数据类型. 2.虽然只能存放一种数据类型,假设A , 如果数据类型B 继承A,依然能存放进入数组. 3.数组的初始化 ...

  2. 20个命令行工具监控 Linux 系统性能(转载)

    1. top — Linux 系统进程监控 top 命令是性能监控程序,它可以在很多 Linux/Unix 版本下使用,并且它也是 Linux 系统管理员经常使用的监控系统性能的工具.Top 命令可以 ...

  3. python 面向对象深入理解

    面向过程 函数式编程 面向对象编程:面向对象是由类和对象组成,只要用类和对象实现的,就是面向对象编程 def  Bar():     print  "This is  Bar " ...

  4. 服務器提交協議衝突 (The server committed a protocol violation.)

    ---解決方法 (放在 app.config / web.config)--- <system.net> <settings> <httpWebRequest useUn ...

  5. 【转】Java中equals和==的区别

    [转]Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boole ...

  6. PHP获得header头进行分析

    学web的人都知道,要深刻的理解就一定要对HTTP协议有深刻的理解,这样你才能理解整个运行的流程,有些功能你才能理解应该 如何去实现,比如:仿盗链啊,定义IP后切换页面语种的版本啊,等等, 这里就来对 ...

  7. Java 之文件IO编程 之读取

    package com.sun; /* * 这里是对文件IO流读取的操作 * 2014-08-10 */ import java.io.*; public class File_test { publ ...

  8. 多线程、Socket

      多线程 线程.进程和应用程序域 进程:进程是一个操作系统上的概念,用来实现多任务并发执行,是资源分配的最小单元,各个进程是相互独立的,可以理解为执行当中的程序,在操作系统中一般用一个称为PCB的结 ...

  9. Visual 2012 常用快捷键

    快捷键 功能说明 Crtl+K,Crtl+C 注释光标所在行,或选中行 Crtl+K,Crtl+U 反注释光标所在行,或选中行 Crtl+K,Crtl+F 格式化全文 F12 转到定义 Shift + ...

  10. CoreAnimation

    CoreAnimation 1.CABasicAnimation // position CABasicAnimation *ba = [CABasicAnimation animationWithK ...