二,消息收发的更多方式,离线推送与消息同步,多设备登录
本章导读
在前一章从简单的单聊、群聊、收发图文消息开始里面,我们说明了如何在产品中增加一个基本的单聊/群聊页面,并响应服务端实时事件通知。接下来,在本篇文档中我们会讲解如何实现一些更复杂的业务需求,例如:
- 支持消息被接收和被阅读的状态回执,实现「Ding」一下的效果
- 发送带有成员提醒的消息(@ 某人),在超多用户群聊的场合提升目标用户的响应积极性
- 支持消息的撤回和修 改
- 解决成员离线状态下的推送通知与重新上线后的消息同步,确保不丢消息
- 支持多设备登录,或者强制用户单点登录
- 扩展新的消息类型
消息收发的更多方式
在一个偏重工作协作或社交沟通的产品里,除了简单的消息收发之外,我们还会碰到更多需求,例如:
- 在消息中能否直接提醒某人,类似于很多 IM 工具中提供的 @ 消息,这样接收方能更明确地知道哪些消息需要及时响应;
- 消息发出去之后才发现内容不对,这时候能否修改或者撤回?
- 除了普通的聊天内容之外,是否支持发送类似于「XX 正在输入」这样的状态消息?
- 消息是否被其他人接收、读取,这样的状态能否反馈给发送者?
- 客户端掉线一段时间之后,可能会错过一批消息,能否提醒并同步一下未读消息?
等等,所有这些需求都可以通过即时通讯服务解决,下面我们来逐一看看具体的做法。
@ 成员提醒消息
在一些多人聊天群里面,因为消息量较大,很容易就导致某一条重要的消息没有被目标用户看到就被刷下去了,所以在消息中「@成员」是一种提醒接收者注意的有效办法。在微信这样的聊天工具里面,甚至会在对话列表页对有提醒的消息进行特别展示,用来通知消息目标高优先级查看和处理。
一般提醒消息都使用「@ + 人名」来表示目标用户,但是这里「人名」是一个由 应用层决定的属性,可能有的产品使用全名,有的使用昵称,并且这个名字和即时通讯服务里面标识一个用户使用的 clientId 可能根本不一样(毕竟一个是给人看的,一个是给机器读的)。使用「人名」来圈定用户,也存在一种例外,就是聊天群组里面的用户名是可以改变的,如果消息发送的时候「王五」还叫「王五」,等发送出来之后他恰好同步改成了「王大麻子」,这时候接收方的处理就比较麻烦了。还有第三个原因,就是「提醒全部成员」的表示方式,可能「@all」、「@group」、「@所有人」都会被选择,这是一个完全依赖应用层 UI 的选项。
所以「@ 成员」提醒消息并不能简单在文本消息中加入「@ + 人名」,解决方案是给普通消息(LCIMMessage)增加两个额外的属性:
mentionList,是一个字符串的数组,用来单独记录被提醒的clientId列表;mentionAll,是一个Bool型的标志位,用来表示是否要提醒全部成员。
带有提醒信息的消息,有可能既有提醒全部成员的标志,也还单独设置了 mentionList,这由应用层去控制。发送方在发送「@ 成员」提醒消息的时候,如何输入、选择成员名称,这是业务方 UI 层面需要解决的问题,即时通讯 SDK 不关心其实现逻辑,SDK 只要求开发者在发送一条「@ 成员」消息的时候,调用 mentionList 和 mentionAll 的 setter 方法,设置正确的成员列表即可。示例代码如下:
- Unity
- Android
- iOS
LCIMTextMessage textMessage = new LCIMTextMessage("@Tom 早点回家") {
MentionIdList = new string[] { "Tom" }
};
await conversation.Send(textMessage);
String content = "@Tom 早点回家";
LCIMTextMessage message = new LCIMTextMessage();
message.setText(content);
List<String> list = new ArrayList<>(); // 部分用户的 mention list,你可以像下面代码这样来填充
list.add("Tom");
message.setMentionList(list);
imConversation.sendMessage(message, new LCIMConversationCallback() {
@Override
public void done(LCIMException e) {
}
});
LCIMMessage *message = [LCIMTextMessage messageWithText:@"@Tom 早点回家" attributes:nil];
message.mentionList = @[@"Tom"];
[conversation sendMessage:message callback:^(BOOL succeeded, NSError * _Nullable error) {
/* 一条提及 Tom 的消息已发出 */
}];
或者也可以通过设置 mentionAll 属性值提醒所有人:
- Unity
- Android
- iOS
LCIMTextMessage textMessage = new LCIMTextMessage("@all") {
MentionAll = true
};
await conv.Send(textMessage);
String content = "@all";
LCIMTextMessage message = new LCIMTextMessage();
message.setText(content);
boolean mentionAll = true; // 指示是否提及了所有人
message.mentionAll(mentionAll);
imConversation.sendMessage(message, new LCIMConversationCallback() {
@Override
public void done(LCIMException e) {
}
});
LCIMMessage *message = [LCIMTextMessage messageWithText:@"@all" attributes:nil];
message.mentionAll = YES;
[conversation sendMessage:message callback:^(BOOL succeeded, NSError * _Nullable error) {
/* 一条提及所有用户的消息已发出 */
}];
对于消息的接收方来说,可以通过调用 mentionList 和 mentionAll 的 getter 方法来获得提醒目标用户的信息,示例代码如下:
- Unity
- Android
- iOS
jerry.onMessage = (conv, msg) => {
List<string> mentionIds = msg.MentionIdList;
};