关于项目第三方库的选用

大家都知道

Android 项目开发,几乎都需要使用一些第三方库。网络、图片加载、JSON 解析,如果自己造轮子,会花费大量时间,而且自己的轮子在稳定性、性能等方面很可能比不过久经考验的开源库。

Too Long; Don’t Read

最近的一个项目实在无法吐槽,维护一个不爱编程只想混日子的程序猿的代码,实话说挺痛苦的,经常要挑战自己的接受能力。每个人都可以选择自己的生活,我也不好说什么。今天只说一点遇到的问题。我们的应用在某些机型上莫名其妙地崩溃,后台查看日志后,发现是图片加载库 bitmapfun 中 DiskLruCache 的 NullPointerException。由于代码已经被混淆不能精确定位到行,没有能复现 bug 的设备,也无法进行测试,第一反应自然是要更新 bitmapfun,看看是否别人已经遇到过类似问题并已解决。而且为了以后更新库方便,必然优先使用库的最近版本,而非自己直接就去改,以至于因为一个 bug 自己维护一个分支。
之前我遇到过 bitmapfun 的一个 NPE,在手机剩余存储空间比较小时触发。搜索 bitmapfun 的最新版本时,竟然找不到代码托管于何处。jcenter maven 里都是没有的。只能看到几篇博客介绍说它是谷歌的官方教程示例代码,Google “bitmapfun” 的第一条结果是 googlesource 上 2013 年的一个提交,而 master 分支的对应目录下并没有 bitmapfun。这时已经有点怀疑人生了。这个 NPE 是开发时遇到的必现 bug,因此跑一遍 debug 就能找到问题所在,时间不等人,于是先在自己本地代码中修复了。但是,找不到社区反馈,找不到代码托管去提 issue、push request,心里还是不爽。
今天又遇到问题,一定要好好看看这个 library 到底是什么鬼。Google 不到有价值的结果,就在 googlesource 里面翻找。直接说结果。bitmapfun 于 Tue Apr 03 23:15:18 2012 -0400 被添加进 git repository于 Wed Feb 26 00:34:41 2014 +0000 被复制到所谓 samples framework于 Wed Feb 26 14:31:33 2014 +1100 在原目录中移除,最新代码在此,同时在 GitHub 上也能找到。也就是说,这只是一个加载图片的例子而已,并不是一个成熟的图片加载框架,对此我只能表示��U+1F602。而最大的槽点是,这个项目用的是最最原始的 Apr 03 23:15:18 2012 -0400 这一版。话说这次出问题的不是 DiskLruCache 类么,在 DiskLruCache.java 中清楚明白地写着

/**
* A simple disk LRU bitmap cache to illustrate how a disk cache would be used for bitmap caching. A
* much more robust and efficient disk LRU cache solution can be found in the ICS source code
* (libcore/luni/src/main/java/libcore/io/DiskLruCache.java) and is preferable to this simple
* implementation.
*/
via. https://android.googlesource.com/platform/development/+/2bab0137e0af92c374d3efb2ed0e1d894981e015/samples/training/bitmapfun/src/com/example/android/bitmapfun/util/DiskLruCache.java#42

这个问题在项目初期是很容易避免的,现在才改无疑大大增加了成本。

一些想法

第三方库的选用一定要慎重(点题……)。目前想到的有几个方面。稳定性、性能需要评测,但是更新频率、社区活跃度、issue 反馈时长等信息,则是比较容易获得的。最最基本的,当有稳定版新版发布时,你应该用经过了各种修复优化的最新稳定版本,而非4年之前的。
更进一步,你可以自定义一层 API,在应用内所有需要相关处理的地方都使用自己写的调用方式,然后保证可以使用不同的库实现你定义的 API。以网络访问为例,你想做的不过是请求一个 URL,然后拿到接口返回的数据。于是你可以定义自己的 Request、Callback 等类。伪代码就像这样(伪得很彻底,类名方法名都是随便写的):

MyHttpUrl url = new MyHttpUrl();
MyParam param = new MyParam();
MyCallback<MyModel> callback = MyCallback<>() {
    onSuccess(MyModel model) { /* use the parsed model */ }
    onFailure(Error err) {/* log or toast */}
};
MyRequest req = new MyRequest.Builder(url, param, callback)
    .setTimeOut(TIME_OUT)
    .setParser(new MyParser(MyModel.class))
    .setXXX(...)
    .build();
MyNetwork.send(req);

MyHttpUrl是预定义的一组枚举(或者对象),一个MyHttpUrl包含一个 String URL、与 URL 对应的 HTTP 方法(GET POST etc.)、接口预期的成功返回和失败返回分别对应的 JSON model 类,等等。调用此代码段,然后你就可以在回调中直接获得解析过的 model (或者叫 entity) 类。需要注意的是,回调中的 onSuccess onFailure 等方法都至少要有一个对象参数,而不能只是基本数据类型的堆砌。例如,onFailure(Error err)onFailure(Error err, int errCode, String msg) 等都是可以接受的;而 onFailure(int errCode, String msg) 就会面临难以扩展的问题:onFailure每增加一个字段,前者的写法可以通过在 Error 类中多写一个 field 完成,不影响既有代码,而后者的写法需要修改之前所有相同形式的调用。
而在这层 API 的封装之下,你可以用任意第三方网络库去实现这层 API,okhttp、Volley、Retrofit、甚至 HttpURLConnection、已被弃用的 HttpClient。如此,当更换网络库、第三方库进行不兼容的大版本更新等情形时,可以把维护成本降到最低。

偶然所得

https://alexpeattie.com/blog/working-with-dates-in-git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值