小红书Android客户端第一个版本于2014年8月8日发布,转眼到了2016年8月8日,小红书Android版本发版两周年。趁机回顾一下小红书的Android版本,两年中我们踩过很多坑,收获很多经验,分享出来与大家共勉。
小红书从最初1.0到现在目前4.7版本,历经两年,安装包从原先的5M发展到现在的17M,产品模块也从原先的只有社区模块发展到了具有社区和电商两个大模块。App包含社区、电商、支付、推送、直播、统计等各种功能和模块,那么开始吧。

功能演进

两年的时间,30多个版本的迭代,许多功能都有了翻天覆地的变化。我们的新人欢迎页也是从最初的比较炫的效果发展到目前比较稳定的简洁版本。当初钟大侠花了无数个日日夜夜,苦心做出来了多个欢迎页动画,虽然现在已经不再使用,但是我们也学习到了一些新技术。后来,钟大侠还是将其贡献到了github开源社区中。
欢迎页第一版

下载地址:https://github.com/w446108264/XhsParallaxWelcome

欢迎页第二版

下载地址: https://github.com/w446108264/XhsWelcomeAnim

社区是小红书的核心价值之一,笔记是小红书社区的核心体现,毋庸置疑,笔记发布是小红书App的核心功能之一,我们一直在产品和技术上,优化我们的笔记发布流程和功能,包括我们将只支持分享单张图片,扩展到现在支持多张图片同时发布。同时支持更丰富的图片编辑效果,更加便捷的发布笔记。

小红书的笔记展现形式和大多数其他的图片社交App类似,我们也支持图上标签功能。最初小红书图上标签是同其他App类似的黑色的标签。不过在3.0之后,小红书创造了独特的树状标签,给用户带来焕然一新的体验,同时也被其他App竞相模仿。新的标签给技术也带了很多的挑战,我们重新定义了标签的结构,以及标签的生成和展示。可以查看我以前的博客,来看看我是怎样做标签的动画的。(https://blog.isming.me/2016/06/07/path-property-animation/

UI的改版,功能上的改动还有很多,这里不再一一提起。小红书Android整体上的风格和iOS保持一致,不过我们在15年初开始,对于App内的细节进行Material Design 适配,包括一些按钮风格、点击效果、字体规范、对话框等等,希望为Android用户带来更好的使用体验。

技术选型进化

在技术选型上,这里主要讲一下网络层的框架选型升级和图片加载库的升级。

网络框架的演进

App的最初框架是由钟大侠一人花了10来天完成,包括基本的网络请求框架、App大体的架构以及一些主要的功能。最初时候选择框架的原则就是选择自己最熟悉的,因此我们采用了async-http这套框架作为我们底层的网络请求框架,框架完成了网络的异步请求与回调,能够满足当时的需求。

然而仅仅不到半年之后,我们就决定了使用Volley来替换。替换以后,底层的网络请求代码更加清晰,在Volley返回的结果即直接返回了我们需要的Object,同时将统一的错误处理、公共的参数处理和一些公共的返回使用的参数,全部放在我们自定义的Request当中,这样外部请求所需要传入的参数更少,对于错误的处理更加简单,只需要考虑业务需要的Response,其他全局的返回内容则无需进行干扰。通过Volley的引入,帮助我们在业务的开发上变得更加便捷。引入Volley之初,Volley的底层使用的是HttpClient+HttpURLConnection,后期通过网上的资料发现OkHttp使用NIO更加高效,并且被Android 引入作为系统底层的网络请求,我们也将Volley的底层也替换为OkHttp。

与此同时,小红书的api请求也在不断进行RESTful,我们遇到一个问题就是经常找一个api的定义比较麻烦。大约在15年11月份,我们引入了Retrofit,通过二次改造,使其支持了公共参数的构建,以及对于GsonConvert的改进支持直接返回我们需要的Object,而且对于RESTful风格的良好支持给我们提供了极大的便利。配合RxJava,我们可以方便的进行多个api的同时请求、api返回的多个线程的切换。

图片加载框架的演进

小红书的笔记是以图片加文字为主体的内容,因此会有大量的图片显示需求。和网络框架选型类似,早期选择了比较熟悉的UIL来做图片加载,可以同时支持本地图片和网络图片的加载,在当时可以满足我们的基本需求。

15年初,我们开始使用更加高清的图片,随之加载速度变慢,占用更多的内存,而且这个时候UIL的作者基本很少维护。我们开始调研使用新的图片加载框架。此时Fresco刚刚出来,还不太稳定,当时没敢用。给我们的可选项有Picasso和Glide两个可选项,Picasso比较轻量,但是相比于UIL在性能上没有太好的提高。Glide代码量较大,不过它会在本地保存多份缓存(原始图片和实际显示尺寸的图片),这样加载本地缓存的时候,可以直接显示大小刚好的尺寸,减少解码的时间,因此会比UIL要快很多。

15年下半年,我们需要支持gif的动画显示,而Glide对动画的兼容性又不是特别好,这个时候我们直接切到了Fresco。同时Fresco对webp的良好支持,使得我们在后期切换到webp格式的时候,减少了很多工作量。Fresco在4.4及以下版本使用匿名内存来作为内存缓存,为我们减少OOM做了巨大的贡献。

我们使用的这几个图片加载框架,每个框架的使用都有非常大的区别,这就导致迁移的时候工作量巨大。为了降低迁移成本,我们封装了自己的ImageLoader,在ImageLoader中来实现具体的图片加载,这样保证在迁移的时候,最大程度的降低代码的改动(不过在迁移到Fresco的时候还是改动巨大,因为我们不能直接使用ImageView了o(︶︿︶)o。

推送的升级

推送,我觉得也有必要说一说。最初我们快速选用了百度云推送,在当时看来百度的推送比较稳定,同时接入比较简单。实际使用了一年之后,发现送达率不是特别高,并且数据统计做的不太好,无法比较好的统计推送效果。在调研之后,我们决定迁移到小米推送+友盟推送的模式,针对小米用户开启小米推送,其他用户采用友盟推送,为了平滑过渡,在切换期间同时向未升级的老用户继续使用百度云推送进行推送。

架构升级

由于一直以来在业务开发占用的时间比较多,目前App的整体架构没有做过太大的改变。

在Adapter的使用方面,我们将ListView或RecyclerView的Item放到单独的ItemHander,这样可以在不同的页面可以通过将不同的Item组装到一起,从而满足不同地方的需求。这样可以在ListView或RecyclerView来复用相同的代码,提高代码的可维护性。

前面网络层说到我们的错误处理,这个也是做过比较大的升级。最初时候,网络错误、http请求错误、后台和客户端的错误,都分别在不同的层级进行处理。目前我们在发生错误的时候将错误全部以Exception的方式抛出,最后在上层进行错误的处理。

App中的状态同步,早期使用使用数据库缓存部分数据,或者使用LocalBroadcast进行广播通讯,前者有很多的限制,后者使用起来较为复杂。近期我们改用EventBus进行状态同步,同时这样也使得各个页面之间的耦合也低。

App中占比很大的部分是从网络请求数据,获得数据后进行展示,还是以MVC为主。在一些模块的部分地方,做一些databinding,MVP等的测试。后面有机会会更多大范围的重构。

其他周边进化

我们的开发最初是使用Eclipse进行开发的,但是Eclipse仅仅存在了不到一个月。在我苦口婆心的劝说下,钟大侠和我一起切换到了Android Studio。而这导致我们的项目目录一直都是使用Eclipse时代的目录格式,直到今年年初才切换到Android Studio推荐的目录格式,切换完目录为我们做debug和release差异化提供了极大的便利。

APK最初大约只有5M,历史最高峰达到了23M,在App减肥上我们也做了一些努力,主要是使用tinypng压缩图片,so只保留arm的支持。项目的复杂也使得每次编译都变得很慢,关于这个可以看下我以前的gradle加速https://blog.isming.me/2015/03/18/android-build-speed-up/

现在持续集成还是蛮火的,自然我们也在用。最初的时候,我们每天需要手动打包,打完包之后打开fir的网站,将apk传上去,然后在公司的微信群吼一声,告诉大家我们发包了。经历一段时间后,我们编写了一个Gradle插件帮助我们自动上传到fir,在之后我们搭建了Jenkins自动完成这一系列步骤,并通过邮件告知大家,然后就可以愉快的玩耍了。

Jenkins

未完待续

本文介绍了我们两年来的一些大的变化,通过一篇文章可能很多东西还是说不清楚,暂时就写这么多。目前项目的组织架构还没有特别大的变化,我们目前已经在做一些小范围的测试,后面将对继续不断的进化和演进。

现在我们的Android还有坑位,如果你有兴趣,就赶紧发送简历来占坑吧,我的邮箱是: msang#xiaohongshu.com (请手动替换#为@)

本文同步发布于小红书技术微信公众号REDHacker,可如下扫描二维码或搜索id: red-hacker进行关注。

REDHacker

原文地址:http://blog.isming.me/2016/08/08/red-android-evolution/,转载请注明出处。