iOS 列表页面实时刷新解决方案
创始人
2024-02-09 20:59:47

iOS 列表页面实时刷新解决方案

一、背景介绍

1.1 问题的出现

客户要求APP客户端每次切换Tab,都需要从服务器去获取最新的数据,所以每次切换Tab,客户端都会去主动刷新接口,以获取最新的数据。但是实际发现,每次切换Tab都去刷新,从用户体验上感觉刷新太频繁了;从性能上体验,没有数据更新也去频繁的请求接口,很消耗网络资源。于是,客户针对这一情况,提出了新的需求:当服务器有数据更新的时候,此时客户端去主动刷新列表接口以获取最新的数据,否则不需要客户端主动去请求接口,直接显示上一次从服务器请求的数据。这样就既做到了用户每次可以看到最新的数据,而且不影响用户体验和性能。

1.2 问题的解决方案

要实现列表做到服务器有数据更新时去主动请求接口,没有数据更新时则不请求接口,关键是要知道服务器什么时候数据更新了,需要服务器主动告知客户端某个列表的接口有数据更新了,当用户切换到此列表时,需要客户端主动去请求接口,获取最新的数据。要实现这一功能,采用websocket是最合适的。websocket能保持服务器和客户端的通信,当服务器有数据更新了,通过websocket告知用户需要刷新列表了。

二、WebSocket的介绍

WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯。

WebSocket工作原理

  1. 第一步:客户端连接websocket(连接地址 ws://…)

  2. 第二步:服务器向客户端发送心跳包。
    如果客户端能收到服务器发送的心跳包,则表明websocket连接成功。
    只要客户端不断开websocket,就能一直收到服务器发送的心跳包,如果客户端收不到心跳包了,则说明已经断开了连接,此时需要客户端重新去连接websocket。心跳包的存在,其实就是为了监测websocket是否断开连接了。

  3. 第三步:服务器主动推送消息给客户端,客户端根据具体的需求去处理业务逻辑。

  4. 第四步:断开websocket连接。

    当用户退出登录,或者是App退到了后台,此时则需要主动断开websocket,以减少服务器性能的损耗。

以上,就是websocket的工作原理。

三、WebSocket的实际运用

WebSocket的工具类网上有很多,从网上下载一个websocet连接和收发消息的工具类即可。

iOS WebSocket 工具类下载地址:https://www.jianshu.com/p/821b777555d3

3.1 客户端连接websocket

- (void)webSocketLogin
{//请更换websocket连接地址NSString *url = @"ws://121.40.165.18:8800";[[SocketRocketUtility instance] SRWebSocketOpenWithURLString:url];
}

连接成功之后,可以通过如下方法,收到服务器推送过来的websocket消息:

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message  {if (webSocket == self.socket) {NSLog(@"************************** socket收到数据了************************** ");NSLog(@"message:%@", message);}
}

打印日志如下:

************************** socket 连接成功************************** 
message:{"type":"welcome"} //websocket连接成功的提示
message:{"type":"ping","message":1650606091} //心跳包
message:{"type":"ping","message":1650606094} //心跳包
message:{"type":"ping","message":1650606097} //心跳包
...

Websocket连接辅助测试网站:http://www.websocket-test.com/

3.2 判断是否自动刷新的思路

比如,模拟 列表页/关注 列表接口的数据更新,服务器会通过websocket向客户端推送如下消息:

{"message":{"type":"refresh_list", "list_key":"列表/关注","timestamp":1669097371}}

这里主要是通过当前数据更新的时间,与上一次接口刷新的时间对比,然后判断是否需要刷新某个列表的接口。根据接口文档提供的需要自动刷新的列表接口有:

["列表页/关注", "列表页/推荐", "列表页/热点", "列表页/问答"]

3.3 具体实现

大概讲一下我的思路:

  1. 定义一个全局的字典;
  2. 以接口名 (“列表页/关注”)为key,timestamp为value,保存在全局的字典中;
  3. 每刷新成功一次列表接口,将全局字典中接口名对应的值更新一次;
  4. 每次收到websocket数据更新消息,将全局字典中接口名对应的值更新一次;
  5. 用户进入某个列表页面时,取当前系统的时间和全局字典中这个接口对应的最后刷新时间对比,决定是否需要刷新页面。
3.3.1 定义一个全局的字典
#import "ApiNameData.h"@implementation ApiNameData+ (instancetype)sharedInstance
{static ApiNameData *_sharedInstance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{_sharedInstance = [[ApiNameData alloc] init];});return _sharedInstance;
}- (NSMutableDictionary *)apiNameDict
{if(!_apiNameDict){_apiNameDict = [[NSMutableDictionary alloc] init];}return _apiNameDict;
}
3.3.2 定义一个刷新的工具类
@interface RefreshUntil : NSObject+ (RefreshUntil *)shareInstance;- (void)refreshSuccess:(NSString *)apiName;- (BOOL)isNeedRefresh:(NSString *)apiName;- (void)setRefreshTime:(NSNumber *)timestamp withApiName:(NSString *)apiName;- (void)clear;@end
#import "RefreshUntil.h"@implementation RefreshUntil+ (RefreshUntil *)shareInstance {static RefreshUntil *shareInstance = nil;static dispatch_once_t predicate;dispatch_once(&predicate, ^{shareInstance = [[RefreshUntil alloc] init];});return shareInstance;
}- (void)refreshSuccess:(NSString *)apiName
{[[ApiNameData sharedInstance].apiNameDict setObject:[self nowTimeInterval] forKey:apiName];
}- (BOOL)isNeedRefresh:(NSString *)apiName
{NSNumber *timestamp1 = [self nowTimeInterval];NSNumber *timestamp2 = [ApiNameData sharedInstance].apiNameDict[apiName];return [timestamp1 doubleValue]<[timestamp2 doubleValue];
}- (void)setRefreshTime:(NSNumber *)timestamp withApiName:(NSString *)apiName
{[[ApiNameData sharedInstance].apiNameDict setObject:timestamp forKey:apiName];
}- (void)clear
{[[ApiNameData sharedInstance].apiNameDict removeAllObjects];
}//获取当前时间戳
- (NSNumber *)nowTimeInterval
{NSDate *date = [NSDate dateWithTimeIntervalSinceNow:0];NSString *time = [NSString stringWithFormat:@"%.0f", [date timeIntervalSince1970]];NSNumber *timestamp = [NSNumber numberWithDouble:[time doubleValue]];return timestamp;
}@end
3.3.3 每次收到websocket数据更新消息,将全局字典中接口名对应的值更新一次
- (void)receiveRefreshMessage:(NSDictionary *)messageDict
{NSString *list_key = messageDict[@"list_key"];NSNumber *timestamp = messageDict[@"timestamp"];[[RefreshUntil shareInstance] setRefreshTime:timestamp withApiName:list_key];
}
3.3.4 用户进入某个列表页面时,取当前系统的时间和全局字典中这个接口对应的最后刷新时间对比,决定是否需要刷新页面
BOOL isNeedRefresh = [[RefreshUntil shareInstance] isNeedRefresh:@"列表页/关注"];
if(isNeedRefresh==YES){[self.tableView.mj_header beginRefreshing];
}
3.3.5 每刷新成功一次列表接口,将全局字典中接口名对应的值更新一次
[[RefreshUntil shareInstance] refreshSuccess:@"列表页/关注"];

3.4 websocket的连接与断开

需要主动连接websocket的情况:

  1. 用户登录成功之后,主动连接websocket;
  2. APP从后台进入前台的时候,主动连接websocket;

需要主动断开websocket的情况:

  1. 用户退出登录之后,主动断开websocket;
  2. APP从前台进入后台的时候,主动断开websocket;

需要重新连接websocket的情况:

  1. websocket自动断开,客户端收不到服务器的心跳包,需要重新连接websocket

效果图
在这里插入图片描述

iOS 自动刷新Demo下载

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...