Android 库项目依赖管理

当android开发者为他们的项目选择依赖库时,他们不仅仅关注功能,易用性,性能,文档,技术支持。他们也很关心依赖库有多大会增加方法的数量,随着项目的增长,它的依赖库也在增长,开发者对控制他的App的方法数在65k的限制以下感到压力。因为non-release builds,Proguard太慢以致于等待,开发者试图避免瘟疫般的multidex。这就是为什么library作者认为他们的项目的大小很重要。

控制你的library的方法数最简单的方法是不加入任何非必需的依赖库。任何library加入的依赖库会传递性的加入用户的项目中。例如,如果你需要几个简单的工具方法(像关闭资源),不要为使用它去加入Guava依赖,而是自己写或从通过现有依赖库(make sure to give credit!)设法实现它。当你自需要几个方法并没有引入入14k方法,你的用户肯定会感激你。

那并不是说你应该每次都避免使用外部依赖库。你只需要放聪明点,当HTTP 依赖库已经存在时,不要勉为其难去写一个HTTP client,你应该停止浪费可以更好的改善你的library的时间。

除了加入依赖库的简单方法以外,你可以使用下面几种策略保持你library的“身材”,一种策略是声明依赖库时使用 provided scope ,它属于android gradle 构建系统的一部分。与之对立的是compileprovided scope只会在编译时加入依赖库 ,这意味着当用户们build他们的项目时依赖库不会被打包进APK。用户需要在他们的应用的build.gradle中显式声明依赖库本身以至于它在运行时可用。

注释:也可以选择package scope,依赖会被打包到APK但编译时不可用。

下面是几个你想在你的library中使用可选依赖库的原因:

1.依赖库的核心功能只会被你用户的子集用到,在Retrofit 1.x就可以看到,有些地方可以响应式地消费REST调用而不是使用回调,那些想使用RxJava的用户可以加入RxJava依赖库,其他用户不用负担额外依赖库。 自从Retrofit 使用maven构建系统后配置稍有不同,但是想法是相似的。

我应该警告你发现你自己加入的依赖库的功能不是对所有用户都有用,你真的应该考虑那些功能是否应该成为你library的一部分,稍后再详细说明。

2.android框架中已经存在一种解决方案,但是外部library提供一种更高效解决方案。为了更好的性能已加入外部依赖库或愿意承担增长的方法数的用户可以加入依赖库。

最近我无意中在PlacesAutocompleteTextView library发现这个,它内部的HTTP client可以是OkHttpClient或是HttpUrlConnection,前者总体来说性能更高,但是要求添加OkHttp依赖库。如果用户不希望添加它,他会自动回退使用标准库的HttpUrlConnection

为实现它,由resolver(决策者)这个类在运行时决定依赖那个库。例如下面这个类来决定使用哪个HTTP client

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!”);
}
}

当类被加载,通过全路径类名检查OkHttpClientt的可用性,如果抛出ClassNotFoundException,然后我就知道OkHttp没有被用户添加,我们回退到HttpURLConnectionPlacesHttpClient作为包装类了通过内部代码库使用所有实现中一种的接口。同样的方法可以被用到JSON解析,Gson可以作为可选依赖库。

如果平衡性能和大小很重要,这种方法是不错的。一般备用实现需要更多的精力(例如JSON parsing)我推荐一开始使用外部library节省时间然后考虑在后续发布版本加入备用实现。

我先前提到你应该考虑你的library包含哪些功能。如果某个功能不会被几乎所有用户所使用,最好还是不要引入它。这令第一种使用可选依赖的方法更少被采纳。再以Retrofit为例,它在2.x发布版本不再提供响应式地消费REST 调用作为它核心库的一部分。这一功能被移动到分离模块作为他所在的maven特性发布。

同样的,不同response转换器也被分到它们自己的依赖,例如,需要转换JSON response 和已经依赖Gson的Retofit 用户可以添加下面依赖到他们的build.gradle 文件

dependencies {
compile ‘com.squareup.retrofit:converter-gson:2.0.0-beta2’
}

使用不同JSON库像Jackson或需要解析不同数据格式像XML或protobuf buffers的用户可以这样做而不必负担服务所有Retrofit用户的外部依赖库。重要的一点,核心库不会被额外的功能污染并且可以继续专注于要解决首要问题。

如果你发现android开发者会使用你自己写的库,当你设计时使用这些策略。考虑你库的大小不仅仅作attribute,而是一种feature。为此你的用户会由衷的感谢你。

原文链接