从 Socket 接收批量数据时应用冻结。 iOS | Socket.io |领域斯威夫特
App freezing when receiving bulk data from Socket. iOS | Socket.io | RealmSwift
我正在开发一个聊天应用程序,我可以在其中一次接收大量消息,这会导致应用程序冻结。以下是我的套接字接收器:
func receiveNewDirectMessages() {
self.socket?.on(EventListnerKeys.message.rawValue, callback: { (arrAckData, ack) in
print_debug(arrAckData)
guard let dictMsg = arrAckData.first as? JSONDictionary else { return }
guard let data = dictMsg[ApiKey.data] as? JSONDictionary else { return }
guard let chatData = data[ApiKey.data] as? JSONDictionary else { return }
guard let messageId = chatData[ApiKey._id] as? String , let chatId = chatData[ApiKey.chatId] as? String else { return }
if MessageModel.getMessageModel(msgId: messageId) != nil { return }
let isChatScreen = self.isChatScreen
let localMsgId = "\(arc4random())\(Date().timeIntervalSince1970)"
if let senderInfo = data[ApiKey.senderInfo] as? JSONDictionary, let userId = senderInfo[ApiKey.userId] as? String, userId != User.getUserId() {
_ = AppUser.writeAppUserModelWith(userData: senderInfo)
}
let msgModel = MessageModel.saveMessageData(msgData: chatData, localMsgId: localMsgId, msgStatus: 2, seenByMe: false)
let chatModel = ChatModel.saveInboxData(localChatId: msgModel.localChatId, inboxData: chatData)
if isChatScreen {
self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .delivered)
self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .seen)
} else {
ChatModel.updateUnreadCount(localChatId: chatModel.localChatId, incrementBy: 1)
self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .delivered)
}
TabController.shared.updateChatBadgeCount()
})
}
上面发生了什么:
1. 我在这个套接字侦听器中一个接一个地接收所有未送达的消息。
2. 获取消息数据
3.将收到的发件人信息保存到Realm DB
4.将消息模型保存到realm DB
5. SAVING/UPDATING 领域数据库中的聊天线程
6. 对接收到的消息发出确认
7. 更新标签栏上的聊天徽章计数
下面是我用于确认消息传递的发射器。
func emitMessageStatus(msgId: String, chatId: String, socketService: SocketService, status: MessageStatusAction) {
// Create Message data packet to be sent to socket server
var msgDataPacket = [String: Any]()
msgDataPacket[ApiKey.type] = socketService.type
msgDataPacket[ApiKey.actionType] = socketService.listenerType
msgDataPacket[ApiKey.data] = [
ApiKey.messageId: msgId,
ApiKey.chatId: chatId,
ApiKey.userId: User.getUserId(),
ApiKey.statusAction: status.rawValue
]
// send the messsage data packet to socket server & wait for the acknowledgement
self.emit(with: EventListnerKeys.socketService.rawValue, msgDataPacket) { (arrAckData) in
print_debug(arrAckData)
guard let dictMsg = arrAckData.first as? JSONDictionary else { return }
if let msgData = dictMsg[ApiKey.data] as? [String: Any] {
// Update delivered Seen Status here
if let msgId = msgData[ApiKey.messageId] as? String, let actionType = msgData[ApiKey.statusAction] as? String, let msgStatusAction = MessageStatusAction(rawValue: actionType) {
switch msgStatusAction {
case .delivered:
if let deliveredTo = msgData[ApiKey.deliveredTo] as? [[String: Any]] {
_ = MessageModel.updateMsgDelivery(msgId: msgId, deliveredTo: deliveredTo)
}
case .seen:
if let seenBy = msgData[ApiKey.seenBy] as? [[String: Any]] {
_ = MessageModel.updateMsgSeen(msgId: msgId, seenBy: seenBy)
}
case .pin:
MessageModel.clearPinnedMessages(chatId: chatId)
if let pinTime = msgData[ApiKey.pinTime] as? Double {
MessageModel.updatePinnedStatus(msgId: msgId, isPinned: true, pinTime: pinTime)
}
case .unPin:
if let pinTime = msgData[ApiKey.pinTime] as? Double {
MessageModel.updatePinnedStatus(msgId: msgId, isPinned: false, pinTime: pinTime)
}
case .delete:
MessageModel.deleteMessage(msgId: msgId)
case .ackMsgStatus, .like, .unlike:
break
}
}
}
}
}
上面发生了什么:
- 封装所有相关信息以确认事件
- 确认发送后更新领域数据库
现在,我无法在这里违抗完美的线程策略。后台线程写什么,主线程写什么。然而,我尝试这样做,但这会导致随机崩溃或数据包丢失。
任何人都可以就这个话题引导我前进。我将不胜感激。
- 尝试使用后台线程进行数据处理/非UI处理。
减少更新次数UI次
使用去抖动之类的方式,而不是逐条处理消息。您可以存储新消息,然后用 n 条新消息更新 UI。因此,不是将 UI/saving 数据更新到数据库 100 条消息 100 次,而是可以对 100 条消息执行 1 次。更多细节:对于每条新消息,将其添加到数组中。调用去抖器。 Debouncer 会延迟函数调用,每次调用它时都会延迟前面的调用,直到延迟时间结束。所以在例如 200ms 之后,如果没有新消息,将调用更新函数(去抖动处理的回调函数)。然后用 n 条新存储的消息更新 ui/db。
您可以按时间对消息进行分组,例如按 1 小时分组。然后在每个时间组之间延迟更新。您可以在调用去抖器时执行此操作 -> 按时间分组消息 -> 每组更新 db/ui。可以使用setTimeout,比如更新组1,100ms后更新组2,这样ui就不会被冻结
我正在开发一个聊天应用程序,我可以在其中一次接收大量消息,这会导致应用程序冻结。以下是我的套接字接收器:
func receiveNewDirectMessages() {
self.socket?.on(EventListnerKeys.message.rawValue, callback: { (arrAckData, ack) in
print_debug(arrAckData)
guard let dictMsg = arrAckData.first as? JSONDictionary else { return }
guard let data = dictMsg[ApiKey.data] as? JSONDictionary else { return }
guard let chatData = data[ApiKey.data] as? JSONDictionary else { return }
guard let messageId = chatData[ApiKey._id] as? String , let chatId = chatData[ApiKey.chatId] as? String else { return }
if MessageModel.getMessageModel(msgId: messageId) != nil { return }
let isChatScreen = self.isChatScreen
let localMsgId = "\(arc4random())\(Date().timeIntervalSince1970)"
if let senderInfo = data[ApiKey.senderInfo] as? JSONDictionary, let userId = senderInfo[ApiKey.userId] as? String, userId != User.getUserId() {
_ = AppUser.writeAppUserModelWith(userData: senderInfo)
}
let msgModel = MessageModel.saveMessageData(msgData: chatData, localMsgId: localMsgId, msgStatus: 2, seenByMe: false)
let chatModel = ChatModel.saveInboxData(localChatId: msgModel.localChatId, inboxData: chatData)
if isChatScreen {
self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .delivered)
self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .seen)
} else {
ChatModel.updateUnreadCount(localChatId: chatModel.localChatId, incrementBy: 1)
self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .delivered)
}
TabController.shared.updateChatBadgeCount()
})
}
上面发生了什么: 1. 我在这个套接字侦听器中一个接一个地接收所有未送达的消息。 2. 获取消息数据 3.将收到的发件人信息保存到Realm DB 4.将消息模型保存到realm DB 5. SAVING/UPDATING 领域数据库中的聊天线程 6. 对接收到的消息发出确认 7. 更新标签栏上的聊天徽章计数
下面是我用于确认消息传递的发射器。
func emitMessageStatus(msgId: String, chatId: String, socketService: SocketService, status: MessageStatusAction) {
// Create Message data packet to be sent to socket server
var msgDataPacket = [String: Any]()
msgDataPacket[ApiKey.type] = socketService.type
msgDataPacket[ApiKey.actionType] = socketService.listenerType
msgDataPacket[ApiKey.data] = [
ApiKey.messageId: msgId,
ApiKey.chatId: chatId,
ApiKey.userId: User.getUserId(),
ApiKey.statusAction: status.rawValue
]
// send the messsage data packet to socket server & wait for the acknowledgement
self.emit(with: EventListnerKeys.socketService.rawValue, msgDataPacket) { (arrAckData) in
print_debug(arrAckData)
guard let dictMsg = arrAckData.first as? JSONDictionary else { return }
if let msgData = dictMsg[ApiKey.data] as? [String: Any] {
// Update delivered Seen Status here
if let msgId = msgData[ApiKey.messageId] as? String, let actionType = msgData[ApiKey.statusAction] as? String, let msgStatusAction = MessageStatusAction(rawValue: actionType) {
switch msgStatusAction {
case .delivered:
if let deliveredTo = msgData[ApiKey.deliveredTo] as? [[String: Any]] {
_ = MessageModel.updateMsgDelivery(msgId: msgId, deliveredTo: deliveredTo)
}
case .seen:
if let seenBy = msgData[ApiKey.seenBy] as? [[String: Any]] {
_ = MessageModel.updateMsgSeen(msgId: msgId, seenBy: seenBy)
}
case .pin:
MessageModel.clearPinnedMessages(chatId: chatId)
if let pinTime = msgData[ApiKey.pinTime] as? Double {
MessageModel.updatePinnedStatus(msgId: msgId, isPinned: true, pinTime: pinTime)
}
case .unPin:
if let pinTime = msgData[ApiKey.pinTime] as? Double {
MessageModel.updatePinnedStatus(msgId: msgId, isPinned: false, pinTime: pinTime)
}
case .delete:
MessageModel.deleteMessage(msgId: msgId)
case .ackMsgStatus, .like, .unlike:
break
}
}
}
}
}
上面发生了什么:
- 封装所有相关信息以确认事件
- 确认发送后更新领域数据库
现在,我无法在这里违抗完美的线程策略。后台线程写什么,主线程写什么。然而,我尝试这样做,但这会导致随机崩溃或数据包丢失。
任何人都可以就这个话题引导我前进。我将不胜感激。
- 尝试使用后台线程进行数据处理/非UI处理。
减少更新次数UI次
使用去抖动之类的方式,而不是逐条处理消息。您可以存储新消息,然后用 n 条新消息更新 UI。因此,不是将 UI/saving 数据更新到数据库 100 条消息 100 次,而是可以对 100 条消息执行 1 次。更多细节:对于每条新消息,将其添加到数组中。调用去抖器。 Debouncer 会延迟函数调用,每次调用它时都会延迟前面的调用,直到延迟时间结束。所以在例如 200ms 之后,如果没有新消息,将调用更新函数(去抖动处理的回调函数)。然后用 n 条新存储的消息更新 ui/db。
您可以按时间对消息进行分组,例如按 1 小时分组。然后在每个时间组之间延迟更新。您可以在调用去抖器时执行此操作 -> 按时间分组消息 -> 每组更新 db/ui。可以使用setTimeout,比如更新组1,100ms后更新组2,这样ui就不会被冻结