获取历史消息
获取历史消息
本文介绍环信即时通讯 IM Flutter SDK 如何从服务器和本地获取历史消息。
环信即时通讯 IM 提供消息漫游功能,即将用户的所有会话的历史消息保存在消息服务器,用户在任何一个终端设备上都能获取到历史信息,使用户在多个设备切换使用的情况下也能保持一致的会话场景。
SDK 内部使用 SQLite 保存本地消息,你可以获取本地消息。
技术原理
环信即时通讯 IM Flutter SDK 通过 EMChatManager 和 EMConversation 类实现对本地消息的管理,其中核心方法如下:
EMChatManager#fetchHistoryMessages:根据FetchMessageOptions类获取服务器保存的指定会话中的消息。EMChatManager#doAsyncFetchHistoryMessages:从服务器获取指定群成员发送的消息;EMConversation#asyncSearchMsgFromDB:从本地获取指定群成员发送的消息;EMChatManager.getConversation:读取本地指定会话的消息。EMChatManager.loadMessage:根据消息 ID 获取消息。EMConversation.loadMessagesWithMsgType:获取本地存储的指定会话中特定类型的消息。EMConversation.loadMessagesFromTime:获取一定时间段内本地指定会话中发送和接收的消息。EMChatManager.loadConversationMessagesWithKeyword:根据关键字获取指定会话中的消息。
前提条件
开始前,请确保满足以下条件:
实现方法
从服务器获取指定会话的消息
你可以调用 fetchHistoryMessagesByOption 方法基于 FetchMessageOptions 类从服务端分页拉取单聊和群组聊天的历史消息(消息漫游)。为确保数据可靠,我们建议你每次获取 20 条消息,最大不超过 50。分页查询时,若满足查询条件的消息总数大于 pageSize 的数量,则返回 pageSize 数量的消息,若小于 pageSize 的数量,返回实际条数。消息查询完毕时,返回的消息条数小于 pageSize 的数量。
通过设置 FetchMessageOptions 类,你可以根据以下条件拉取历史消息:
- 消息发送方;
- 消息类型;
- 消息时间段;
- 消息搜索方向;
- 是否将拉取的消息保存到数据库;
- 对于群组聊天,你可以设置
from参数拉取群组中单个成员发送的历史消息。
提示
- 若使用该 API,需将 SDK 版本升级至 V4.0.2 版本或以上。
- 默认可获取单聊和群组聊天的历史消息。若要获取聊天室的历史消息,需升级至 4.5.0 版本,并联系环信商务。
- 获取单聊历史消息时会读取服务端保存的消息送达状态和已读状态。该功能默认关闭,如果需要,请联系环信商务开通。
- 历史消息在服务器上的存储时间与产品的套餐包相关,详见 IM 套餐包功能详情。
// fromUserId: 消息发送方的用户 ID。
// msgTypes: 消息类型,可以是 [MessageType.TXT, MessageType.IMAGE, MessageType.VIDEO, MessageType.LOCATION, MessageType.VOICE, MessageType.FILE, MessageType.CUSTOM, MessageType.COMBINE]
// startTs: 查询开始时间戳。
// endTs: 查询结束时间戳。
// needSave: 是否需要保存到本地数据库。
FetchMessageOptions options = FetchMessageOptions(
from: fromUserId,
msgTypes: [MessageType.TXT],
startTs: 1709284487000,
endTs: 1709284499000,
needSave: true,
);
// conversationId: 会话 ID:单聊为对方用户 ID, 群聊时为群组 ID,聊天室聊天为聊天室 ID。
// type: 会话类型:Chat 为单聊;GroupChat 为群聊;ChatRoom 为聊天室。
// options: 查询条件。
// cursor: 分页查询时的游标, 首次可以传 null 或不传,如果是分页查询,传上一次查询结果的游标 result.cursor。
// pageSize: 每页查询的消息数量。
EMCursorResult<EMMessage> result =
await EMClient.getInstance.chatManager.fetchHistoryMessagesByOption(
conversationId,
type,
options: options,
cursor: cursor,
pageSize: 20,
);
此外,你也可以调用 fetchHistoryMessages 方法从服务器获取指定会话的消息。
为确保数据可靠,我们建议你多次调用该方法,且每次获取的消息数小于 50 条。获取到数据后,SDK 会自动将消息更新到本地数据库。
try {
// 会话 ID
String convId = "convId";
// 会话类型:Chat 为单聊;GroupChat 为群聊;ChatRoom 为聊天室。
EMConversationType convType = EMConversationType.Chat;
// 获取的最大消息数
int pageSize = 10;
// 搜索的起始消息 ID
String startMsgId = "";
EMCursorResult<EMMessage?> cursor =
await EMClient.getInstance.chatManager.fetchHistoryMessages(
conversationId: convId,
type: convType,
pageSize: pageSize,
startMsgId: startMsgId,
);
} on EMError catch (e) {
}
从服务器获取指定群成员发送的消息
自 4.15.0 版本开始,对于单个群组会话,你可以从服务器获取指定成员(而非全部成员)发送的消息。
EMCursorResult<EMMessage> result =
await EMClient.getInstance.chatManager.fetchHistoryMessagesByOption(
'conversationId',
EMConversationType.GroupChat,
options: const FetchMessageOptions(senders: ['senderA', 'senderB']),
);
从本地获取指定群成员发送的消息
自 4.15.0 版本开始,对于单个群组会话,你可以从本地获取指定成员(而非全部成员)发送的消息。
List<EMMessage> list = conversation.loadMessagesWithKeyword(
keywords: keywords,
senders: ['senderA, senderB'],
);
读取指定会话的消息
你可以根据会话 ID 和会话类型调用 API 获取本地会话:
// 会话 ID。
String convId = "convId";
// 如果会话不存在是否创建。设置为 `true`,则会返回会话对象。
bool createIfNeed = true;
// 会话类型。详见 `EMConversationType` 枚举类型。
EMConversationType conversationType = EMConversationType.Chat;
// 执行操作。
EMConversation? conversation =
await EMClient.getInstance.chatManager.getConversation(
convId,
conversationType,
true,
);
List<EMMessage>? list = await conversation?.loadMessages();
根据消息 ID 获取消息
你可以调用 loadMessage 方法根据消息 ID 获取本地存储的指定消息。如果消息不存在会返回空值。
// msgId:要获取消息的消息 ID。
EMMessage? msg = await EMClient.getInstance.chatManager.loadMessage("msgId");
获取指定会话中特定类型的消息
你可以调用 loadMessagesWithMsgType 方法从本地存储中获取指定会话中特定类型的消息。
每次最多可获取 400 条消息。若未获取到任何消息,SDK 返回空列表。
EMConversation? conv =
await EMClient.getInstance.chatManager.getConversation("convId");
List<EMMessage>? list = await conv?.loadMessagesWithMsgType(
// 消息类型。
type: MessageType.TXT,
// 每次获取的消息数量。取值范围为 [1,400]。
count: 50,
// 消息搜索方向:(默认)`UP`:按消息时间戳的逆序搜索;`DOWN`:按消息时间戳的正序搜索。
direction: EMSearchDirection.Up,
);
获取指定会话中一定时间段内的消息
你可以调用 loadMessagesFromTime 方法从本地存储中获取指定的单个会话中一定时间内发送和接收的消息。
每次最多可获取 400 条消息。
EMConversation? conv =
await EMClient.getInstance.chatManager.getConversation("convId");
List<EMMessage>? list = await conv?.loadMessagesFromTime(
// 查询的起始时间戳,单位为毫秒。
startTime: startTime,
// 查询的结束时间戳,单位为毫秒。
endTime: endTime,
// 每次获取的消息数量。取值范围为 [1,400]。
count: 50,
);
获取会话在一定时间内的消息数
你可以调用 loadMessagesFromTime 方法从 SDK 本地数据库中获取会话在某个时间段内的全部消息数。
EMConversation? conversation =
await EMClient.getInstance.chatManager.getConversation(conversationId);
if (conversation != null) {
List<EMMessage> messages = await conversation.loadMessagesFromTime(
startTime: startMs,
endTime: endMs,
);
}
根据关键字获取会话中的消息
自 SDK 4.15.2 开始,你可以通过设置关键字获取单个会话中的某些消息。SDK 返回会话 ID 及消息 ID 列表的 Map,消息 ID 根据你设置的 direction 参数按照消息时间戳的正序或倒序列出。
Map<String, List<String>> result =
await EMClient.getInstance.chatManager.loadConversationMessagesWithKeyword(
keyword: "hello", // 搜索包含 "hello" 的消息
timestamp: -1,
sender: null,
direction: EMSearchDirection.Up,
scope: MessageSearchScope.All,
);
调用上述 API 获取到会话 ID 和对应的消息列表后,如果需要使用获取的会话 ID 调用EMChatManager#getConversation 进一步操作,则需将 createIfNeed 参数设置为 false(默认为 true)。 原因是上述 API 获取到的会话 ID 从消息中得到的,因此并不能证明该会话是存在的 (有可能已被删除)。所以,你在调用 EMChatManager#getConversation 时传入了获取的会话 ID,则需将 createIfNeed 参数设置为 false(默认为 true),同时还需对 getConversation 进行是否为空的判断,避免创建会话错误。例如: 调用 loadConversationMessagesWithKeyword 获取了群组会话 ID(即群组 ID)且该会话已删除,将该群组 ID 传入了 EMChatManager#getConversation,该 API 中的 createIfNeed 设置为 true,type 设置为 Chat,则 SDK 会创建单聊会话。
