本文来自网易云社区

作者:王鲁才

客户端开发中不可避免的需要接触到访问网络的需求,如何把访问网络模块设计的更具有扩展性是每一个移动开发者不得不面对的事情。现在有很多主流的网络请求处理框架,如Square公司的OkHttp,Google推出的Volley,还有在OkHttp基础上进行封装的Retrofit等,这些都是非常优秀的网络处理框架。利用现有网络处理框架,比从零开始设计、开发网络请求节省很多开发时间,同时也避免了一些意想不到的问题。如果把这些框架直接拿来使用,不进行任何二次封装,会使我们工程层次结构不清晰(数据存取、逻辑处理、UI展示),不利于扩展(替换成其他网络框架)。所以选择一个合适的网络框架并进行封装,这在开发过程中是非常必要的。

1.   概述

通常情况下,客户端数据流遵循如下图所述方式流动,UI层负责展示数据,接收用户操作;逻辑处理层处理用户操作,并将处理结果反馈给UI层进行展示;数据层负责数据存取,这里可能会涉及多种方式存取数据:本地缓存中存取数据,向服务器请求存取数据;网络访问层负责从服务器存取数据,数据返回后进行解析,并将结果返回给数据存取层。

2.   考拉Android客户端网络模块设计

下面简要介绍一下考拉Android客户端网络模块设计的变迁历程,在这个过程中我们也踩了不少坑,好在我们把这些坑都填平了,而且正在朝着更好的方向发展。

在考拉项目开始之初,我们对比了Volley、OKHttp等主流网络处理框架,最终选择了Volley作为考拉Android客户端的网络请求模块。Volley是在2013年IO大会上Google推出的异步网络请求框架(和图片加载框架),特别适合数据量小,通信频繁的网络操作。

2.1 存在的问题

1.      封装扩展性差(SPDY,HTTPS);

2.      存在内存泄漏(Volley自己的问题);

3.      数据解析在UI线程中进行,阻塞UI展示;

在这一版本的网络请求封装中存在一个比较严重的问题,数据解析在UI线程中进行,在最初的几个版本中,请求的数据量较小,UI比较简单,阻塞UI线程的这个问题还不是特别明显,但是随着UI越来越复杂,同时请求数据、解析数据的操作也越来越多,阻塞UI线程解析数据,存在丢帧问题。为了解决这个问题,引入了工作线程池,网络请求回来以后,先将数据返回给调用者(UI线程),调用者在解析数据的时候,开启工作线程,数据解析完成后再抛回UI线程,UI线程进行数据展示。在这个过程中,有多次线程切换,线程创建、切换、调度都是需要内存、时间开销的。更要命的是,这个过程对调用者不透明,调用者必须知道当前操作是在哪个线程中进行的。

扩展性差这一点相信很多开发者都有比较痛的感悟。为了兼容不同服务器返回的不同数据格式,在网络请求模块中定义了很多不同参数的回调方法。在请求数据时,每一种回调接口都要对应一个具体的方法(传入的回调接口不同),代码写的比较冗余,条理性、扩展性差,简直要不能直视了。

2.2 重构

为了解决上面两个问题,对网络请求模块进行了重构,引入了泛型数据,重新定义了网络数据返回结构,新增了数据解析器。根据用户传入的不同解析器,将网络返回数据解析成不同类型。在网络数据返回后,直接将数据解析操作从UI线程中抛到工作线程中,这个过程对调用者是透明的。调用者只需要知道网络请求操作和数据解析操作是在工作线程中进行的,回调接口在UI线程中执行就可以。

在这个网络响应回调接口中,采用了泛型,调用者可以返回任意数据结构,为了提高兼容性,在错误回调方法中,不仅传入了错误码和错误原因,而且还额外增加了一个Object类型的数据,供调用者返回其他数据信息。

KoalaStringParser是通用数据解析接口,主要针对有特殊需求的数据解析;对于通用的、与服务器约定好的数据返回格式,可以继承KoalaSimpleStringParser抽象类,实现onSimpleParse抽象方法解析数据。通用数据解析接口和抽象数据解析类,既保证了扩展性,又对通用数据格式有了统一处理。

2.3 数据缓存

如果不考虑数据缓存的话,上面对网络请求的封装基本也能满足我们的要求了。但是如果没有数据缓存,每次都向服务器请求数据,启动新页面,会有时间长短不一的加载过程,没有网络或者网络不好时,会显示一个空白页面告诉用户网络不可用,这降低了用户体验。为了提升用户体验,减少启动页面的等待时间,增加数据缓存是一件很有必要的事情。缓存可以与服务器配合,使用HTTP的缓存机制,也可以在客户端单独使用。因为现在考拉服务器还没有支持缓存,现阶段只能在客户端添加缓存。

上面是在没有服务器支持的情况下,客户端支持的缓存。在启动页面请求数据时,先判断是否允许读取缓存数据。允许读取缓存数据,检查是否存在缓存数据,存在缓存数据,将数据从本地缓存中读取后直接返回给调用者;不存在缓存数据,不做任何处理。在检查缓存数据时,同时向服务器发送请求,获取数据,数据返回后更新缓存数据。这里需要注意的是,回调接口可能会被调用两次,一次是从缓存读取数据的回调,一次是从服务器读取数据的回调。出现两次回调的问题,需要调用者进行区分,在回调方法中添加了当前回调是从本地读取数据的回调还是从网络读取数据的回调标示。

服务端不支持缓存,只在客户端对数据进行缓存处理,在一定程度上提升了用户体验,避免用户多次请求相同页面时出现多次重复加载、长时间等待的问题。这种缓存方式并不能节省用户流量。

2.4 切换到OkHttp

前一段时间项目组提到考拉可能会引入SPDY来优化性能,后面为了解决劫持问题,还可能会上HTTPS,考虑到OkHttp已经内置了对SPDY的支持,而且对HTTPS的封装比较好,未雨绸缪,我们决定从Volley转投OkHttp了,因为之前已经对网络请求进行了封装,所以切换到OkHttp还是比较容易的。

OkHttp具有如下优点:

1.      支持SPDY,共享同一个Socket来处理同一个服务器的所有请求;

2.      如果SPDY不可用,则通过连接池来减少请求延时;

3.      无缝的支持GZIP来减少数据流量;

4.      缓存网络数据,减少重复网络请求;

5.      支持HTTP2;

使用OkHttp既可以在工作线程中请求数据,也可以在UI线程中请求数据,这与Volley只能在工作线程中请求数据不同。在工作线程中请求数据,数据返回后不需要重新开启线程解析数据,数据在请求线程中解析完后,直接抛到UI线程中进行展示。

3. 总结

在设计开发功能模块时,往往为了快速完成功能,忽略了模块的扩展性,当后续功能越来越复杂时,之前设计的功能、接口已经不能满足需求时,就要重新审视模块是否需要重构、优化,不能总在旧的代码上打补丁,造成代码难以阅读、维护。

4.   SPDY简介

SPDY是Google开发的基于传输控制协议(TCP)的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。新协议的功能包括数据流的多路复用、请求优先级以及HTTP报头压缩。SPDY协议在性能方面对HTTP做了很大的优化,语义方面并没有做太大的修改。具体来说是,SPDY使用了HTTP的方法和页眉,但是删除了一些报头并重写了HTTP中管理连接和数据转移格式的部分,基本上兼容HTTP。

网易云免费体验馆,0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 上云、微服务化和DevOps,少走弯路的办法
【推荐】 非对称加密与证书(上篇)
【推荐】 django项目在uwsgi+nginx上部署遇到的坑

网易考拉Android客户端网络模块设计的更多相关文章

  1. 考拉Android统一弹框

    作者:钱成杰 背景 在快速开发的背景下,经历了n个版本后的考拉Android App中已经存在了各种各样看似相同却各有差别的弹框样式.其中包括系统弹框和自定义弹框,并且在线上时常会出现IllegalA ...

  2. 2015年网易考拉海淘android面试

    经朋友推荐,昨天下午去网易杭州公司参加了考拉海淘android客户端的面试.今天回忆一下面试题目,做个整理进行备案. 1.说说JVM垃圾回收机制. 1.1.画了JVM分代回收的图,大致说了下垃圾分代回 ...

  3. megalo -- 网易考拉小程序解决方案

    megalo 是基于 Vue 的小程序框架(没错,又是基于 Vue 的小程序框架),但是它不仅仅支持微信小程序,还支持支付宝小程序,同时还支持在开发时使用更多 Vue 的特性. 背景 对于用户而言,小 ...

  4. rtmp直播拉流客户端EasyRTMPClient设计过程中时间戳问题汇总

    EasyRTMPClient 简介 EasyRTMPClient是EasyDarwin流媒体团队开发.提供的一套非常稳定.易用.支持重连接的RTMPClient工具,以SDK形式提供,接口调用非常简单 ...

  5. Android 客户端设计之环境考虑

    我做过两三个android客户端应用的整体设计和部分的编码,这里仅仅谈一下设计方面的故事(此乃原创2015:11:02). 做客户端设计,首先要考虑应用所在的环境,包括三方面:1 要设计的apk是在一 ...

  6. Android 客户端设计之解决方案

    解决方案,是正对与需求来谈的.一个抽象的需求,需要一个较为上层抽象的解决方案来处理,这是病和药的关系.但是一个解决方案,可能会包含多个功能,每个功能都是解决方案上的一个节点.一个优秀的解决方案必然需要 ...

  7. Android典型界面设计(3)——访网易新闻实现双导航tab切换

    一.问题描述 双导航tab切换(底部区块+区域内头部导航),实现方案底部区域使用FragmentTabHost+Fragment, 区域内头部导航使用ViewPager+Fragment,可在之前博客 ...

  8. Android典型界面设计-访网易新闻实现双导航tab切换

    一.问题描述 双导航tab切换(底部区块+区域内头部导航),实现方案底部区域使用FragmentTabHost+Fragment, 区域内头部导航使用ViewPager+Fragment,可在之前博客 ...

  9. 考拉定时任务框架kSchedule

    此文已由作者杨凯明授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.背景 目前项目中使用的定时任务框架存在下面这些问题 没有统一的定时任务管理平台 目前项目中使用定时任务的 ...

随机推荐

  1. Python ssh连接Linux服务器报Incompatible ssh peer (no acceptable kex algorithm) 解决方法

    python通过ssh连接linux服务器,部分服务器出现如下异常 03:50:48.725 FAIL ftp operation failed, Incompatible ssh peer (no ...

  2. expres webpack es6 babel 构建多页系统开发架构

    开始写点什么... 只是一个思路........

  3. IOS 拼接按钮文字

    NSMutableString *tempAnswerTitle=[[NSMutableString alloc]init]; for(UIButton *answerBtn in self.answ ...

  4. 常用mysql系统参数参考

    http://aaronsa.blog.51cto.com/5157083/1741481

  5. 问题 B: C++习题 对象数组输入与输出

    题目描述 建立一个对象数组,内放n(n<10)个学生的数据(学号.成绩),用指针指向数组首元素,输出第奇数(1,3,5,7)个学生的数据. 输入 n和n个学生的学号.成绩 输出 奇数学生的数据 ...

  6. redis list类型

  7. HTTP 下载文件工具类

    ResponseUtils.java package javax.utils; import java.io.ByteArrayInputStream; import java.io.File; im ...

  8. update_TypeError

    TypeError: ( 'An update must have the same type as the original shared variable ( shared_var=W, shar ...

  9. android动画解析(初级)

    效果图: ObjectAnimator继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的,因此ValueAnimator仍然是整个属性动画当中最核心的一个 ...

  10. 网际协议 IP

    网际协议 网际协议(internet  protocol),简称IP; 概念:TCP/IP网络体系结构中网际层的协议.用以提供无连接的数据服务. 1.IP地址的概念及组成 概念:IP地址就是用来唯一标 ...