小明在wifi环境下体验一款app的时候,各种体验都很不错,show各种自拍照,发各种心情写真,玩得不亦乐乎,以至于他给了这款app一个5星的评价。
Paste_Image.png
然而,有一天,小明去外婆家玩了,外婆家地处于一个偏僻的小山村,那里有每天有小鸟唱歌,每天有鱼儿在河里游荡。。。(wait,似乎扯远了),
Paste_Image.png
but,那里3G,4G信号不怎么好,时有时无,偶尔还是一个2G的信号,稍微差点一个G都不G,
Paste_Image.png
当小明在次体验这款app的时候,因为,他想将自己此刻无比高兴的心情分享到app上时,此时一个发送中。。的菊花在他眼前不停的旋转,挥之不去,此时,小明的内心是崩溃的,
Paste_Image.png
去你妹的菊花,我不知道在发送中吗?还要你提醒我?好吧,好吧,不发送了,我看看我之前发送的心情写照吧,然而,此时另外一个拉取中。。。的菊花再次和小明相遇,最后,小明终于怒了,他决定将评价改为一星,然后卸载了这个app,发誓以后再也不要用了,此时的好心情全被这个app给破坏了。
从这个故事中,我们都知道,之所以出现前后两种体验不一致的原因是网络环境变化了->从之前的WIFI切换到了弱网络了
:用户体验(UX)变得很差了。
Paste_Image.png
因此本文主要想阐述如何设计一款在弱网络下环境下也有较好体验的app,demo的效果是这样的:
2016-10-11 10_09_46.gif
2016-10-11 17_39_47.gif
可以看到,这里模拟的是弱网络情形,我的发送速度很快,网络来不及处理,不过没有关系,我可以狂发,没有收到后台服务器成功的回调之前,发送内容是红色,手偶到成功回调之后,颜色变为绿色了。哪里还有等待后台返回的菊花存在呢?而且,更加重要的是,及时我退出app,当我再次进入的时候,没有发送的内容,依然会继续发送。
那么,这些是如何做的呢?
在开始之前,我们不妨先看看一般情况下,我们是如何和后台服务器交互的:
Paste_Image.png
是的,抽象起来就如同上图所示:简单的描述一下步骤《《《成功》》》的步骤就是:
step1.用户发起一个请求。
step2.服务器收到并处理请求。
step3.服务器返回请求结果,客户端根据返回结果渲染UI。
ok. 然而,网络并不一直是我们的朋友,有时候,网络会变得非常淘气。并非所有的用户能和你一样,一直在有WIFI的环境下养尊处优。
再者,即使网络环境ok,服务器也不一定可靠,也许你的后台服务器正在遭受着黑客猛烈的ddos攻击,此时客户端发起的任何请求服务器都无法响应了。
难道就该因为是这种情况,而给用户一个菊花?“内容正在发送中。。。”,“正在拉取中。。。”,who care about this?
那么,我们该如何做呢?
make your app offline
try1.增加一个持久化层
Paste_Image.png
此时,我们在来看看这个步骤:
step1.用户发起一个请求,直接持久化到本地了,此时通知UI使用持久化本地的对象更新VIEW了。
step2.服务器收到并处理请求。
step3.服务器返回请求结果,客户端根据返回结果更新VIEW。
step4.客户端根据返回结构更新持久化数据。
那么,此时,我们再次看一下,我们那个淘气的朋友---网络,如果此时淘气了,或者我们的服务器跪了,我们会遇到什么?
Paste_Image.png
聪明的你会发现,哦,脏数据了,此时发送的这条内容实际上服务器并不知道,只是在你的本地持久化了而已,其他用户根本感知不到你此刻的兴奋,你只是让用户在自嗨而已,聪明的用户如果发现了他然来只是在自嗨,他一定会很失望,可能会失望到卸载app了。
那么,此时的问题是什么❓
此时问题的更远依然是后台服务不可用导致的,但是,我们并没有告知用户此时在进行网络操作,失败了也没有告知用户,发送失败了,提示他很可能已经自嗨了,所以,我们可以不可以尝试下,如果失败了,重试?
可以是可以,有可能重试一次就成功呢。但是,如果是网络问题,或者服务器无响应了,重试在多次,又有何用?那么,怎么办呢?
分析一下原因吧:
2017-02-151、是因为用户无网络,那么等有网络了,在试试不就OK的。
2、如果是因为服务器跪了,那么等服务器启动了,再重试不就行了,但是,我怎么知道服务器可用了呢?
服务器端可以这么配合,如果服务器崩溃了,等服务器重启动后,可以向客户端推送一条消息,表达服务可用。
综合两种情况,反正客户端就是需要重试,只是有条件的重试而已,比如网络可用了,服务可达了,说来说去,这个好像和JobScheduler有点关系啊,那么我们何不增加JobScheduler支持呢?
try2.增加JobScheduler
大家知道JobScheduler是Added in API level 21才加入的,而且没有提供官方的support 包,不过没有关系,github上为我们提供了两个稳定的,一直有维护的替代项目。
https://github.com/evernote/android-job
https://github.com/yigit/android-priority-jobqueue
那么,此时有了JobScheduler的支持之后,我们在来看看,我们这个简单的架构会发生一些什么样的变化:
Paste_Image.png
1、与服务器的交互交给了job去处理,而job是一个持久化的对象,因此即使用户退出app,当再次启动的时候,可以将job恢复继续run。
2、我们来看看没有网络,或者服务器故障的情况,假入没有网络了,job与服务器交互显然也会失败,然而不同的是,job可以配置为因为网络故障,或者服务器内部错误等等情况的失败,自动尝试重试,如果你还想做复杂点,可以尝试梯度重试,直到成功。
3、如果设置尝试次数达到job配置的上限,此时发送notification通知用户:“自嗨中,是否删除发送,还是继续重试”,让用户感知到他实际没有发送成功。
俗话说的好,百闻不如一见,show me the code。
实在不好意思,git.oa不让自己创建项目了,因此,我将项目放到了github上了。
https://github.com/bravekingzhang/OFFlib
TODO
1、碎片化(分片)上传,UGC内容在真实app中显然不会如此简单,可能包含数张图片,而且,是否还看而已考虑单张图片断点续传。
2、持久化之坑,多用户切换,持久化还能如此做吗?
3、UGC不是一个简单的对象,可能是复杂的对象,ONE TO MANY 。
作者:brzhang