功能描述
拉取历史消息的 API 在类 V2TIMManager
和 V2TIMManager+Message(iOS & Mac)
/ V2TIMMessageManager(Android & Windows)
中。
除了支持单聊、群聊历史消息的拉取外,还提供了高级接口以支持按指定方向拉取、按指定起点和指定时间范围拉取。
除了支持单独拉取本地历史消息外,还支持拉取云端历史消息。
说明:
在拉取云端历史消息时,如果检测到网络异常,SDK 会返回本地存储的历史消息。
本地存储的历史消息无时间限制,但云端存储的历史消息有存储时长的限制:
- 体验版:免费存储 7 天,不支持延长。
- 专业版:免费存储 7 天,支持延长。
- 旗舰版:免费存储 30 天,支持延长。
说明:
- 延长历史消息存储时长是增值服务,您可以登录 即时通信 IM 控制台 修改相关配置,具体计费说明请参加 增值服务资费 。
- 富媒体消息(图片、文件、语音等)对应的文件存储时长,与历史消息存储时长保持一致。
拉取单聊历史消息
您可以调用接口 getC2CHistoryMessageList
(Android / iOS & Mac) 获取单聊历史消息。
在网络正常的情况下会拉取最新的云端数据。如果网络出现异常,SDK 会返回本地存储的历史消息。
如果您仅仅想拉取本地历史消息,可以参考 高级接口 。
本接口支持分页拉取,参考:分页拉取。
示例代码如下:
// 拉取单聊历史消息
// 首次拉取,lastMsg 设置为 null
// 再次拉取时,lastMsg 可以使用返回的消息列表中的最后一条消息
V2TIMManager.getMessageManager().getC2CHistoryMessageList(#your user id#, 20, null, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "fail, " + code + ", " + desc);
}
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
// 记录下次拉取的 lastMsg, 用于下次拉取
V2TIMMessage lastMsg = v2TIMMessages.get(v2TIMMessages.size() - 1);
Log.i("imsdk", "success");
}
});
[V2TIMManager.sharedInstance getC2CHistoryMessageList:#your user id# count:20 lastMsg:nil succ:^(NSArray<V2TIMMessage *> *msgs) {
V2TIMMessage *lastMsg = msgs.lastObject;
NSLog(@"success, %@", msgs);
} fail:^(int code, NSString *desc) {
NSLog(@"fail, %d, %@", code, desc);
}];
拉取群聊历史消息
您可以调用接口 getGroupHistoryMessageList
(Android / iOS & Mac 获取群聊历史消息。
在网络正常的情况下会拉取最新的云端数据。如果网络出现异常,SDK 会返回本地存储的历史消息。
如果您仅仅想拉取本地历史消息,可以参考 高级接口 。
本接口支持分页拉取,参考:分页拉取。
注意:
- 只有会议群(Meeting)才能拉取到进群前的历史消息,更多关于群消息的限制,详见 消息能力差异 。
- 直播群(AVChatRoom)消息不存云端漫游和本地数据库,调用此接口无效。旗舰版客户可以在 即时通信 IM 控制台 配置 “直播群新成员查看入群前消息量”,开启后会在加入直播群成功后,在
onRecvNewMessage
回调中获得入群前消息。最多支持新入群成员查看入群前 24h 产生的消息,最多可查看 20 条。控制台配置详见 直播群新成员查看入群前消息配置。
示例代码如下:
// 拉取群聊历史消息
// 首次拉取,lastMsg 设置为 null
// 再次拉取时,lastMsg 可以使用返回的消息列表中的最后一条消息
V2TIMManager.getMessageManager().getGroupHistoryMessageList(#your group id#, 20, null, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "fail, " + code + ", " + desc);
}
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
// 记录下次拉取的 lastMsg,用于下次拉取
V2TIMMessage lastMsg = v2TIMMessages.get(v2TIMMessages.size() - 1);
Log.i("imsdk", "success");
}
});
[V2TIMManager.sharedInstance getGroupHistoryMessageList:#your group id# count:20 lastMsg:nil succ:^(NSArray<V2TIMMessage *> *msgs) {
V2TIMMessage *lastMsg = msgs.lastObject;
NSLog(@"success, %@", msgs);
} fail:^(int code, NSString *desc) {
NSLog(@"fail, %d, %@", code, desc);
}];
高级功能
高级接口
如果以上的普通接口无法满足您对拉取历史消息的需求,我们还提供了高级接口 getHistoryMessageList
(Android / iOS & Mac / Windows)。
该接口除了支持普通拉取单聊、群聊历史消息外,还支持以下高级特性:
- 支持设置消息拉取时的起点。
- 支持设置拉取消息的时间范围。
- 支持设置拉取消息的位置:从本地拉取、从云端拉取。
- 支持按照指定的方向拉取:往消息时间更老的方向拉取、往消息时间更新的方向拉取。
- 支持拉取本地指定的消息类型:文本、图片、语音、视频、文件、表情、群 tips 消息、合并消息、自定义消息等。
接口原型:
public abstract void getHistoryMessageList(V2TIMMessageListGetOption option, V2TIMValueCallback<List<V2TIMMessage>> callback);
- (void)getHistoryMessageList:(V2TIMMessageListGetOption *)option
succ:(V2TIMMessageListSucc)succ
fail:(V2TIMFail)fail;
virtual void GetHistoryMessageList(const V2TIMMessageListGetOption& option,
V2TIMValueCallback<V2TIMMessageVector>* callback) = 0;
V2TIMMessageListGetOption
类参数说明:
参数 |
含义 |
单聊有效 |
群聊有效 |
是否必填 |
说明 |
getType |
拉取消息的位置及方向,可以设置拉取 本地/云端 的 更老/更新 的消息 |
YES |
YES |
YES |
当设置从云端拉取时,会将本地存储消息列表与云端存储消息列表合并后返回。如果无网络,则直接返回本地消息列表。 |
userID |
拉取指定用户的单聊历史消息 |
YES |
NO |
NO |
拉取单聊消息,需要指定对方的 userID,此时 groupID 传空即可。 |
groupID |
拉取指定群组的群聊历史消息 |
NO |
YES |
NO |
拉取单聊消息,需要指定群聊的 groupID,此时 userID 传空即可。 |
count |
单次拉取的消息数量 |
YES |
YES |
YES |
建议设置为 20,否则可能影响拉取速度 |
messageTypeList |
拉取的消息类型集合 |
YES |
YES |
NO |
1. 只支持本地拉取,即当 getType 为 V2TIM_GET_LOCAL_OLDER_MSG 或 V2TIM_GET_LOCAL_NEWER_MSG 时有效。 2. 如果该字段为空,表示拉取所有的消息类型。 3. 支持的消息类型详见 V2TIMElemType (Android / iOS & Mac / Windows)。 |
lastMsg |
最后一条消息 |
YES |
YES |
NO |
可用于拉取历史消息的场景。 1. 单聊和群聊中均能使用。 2. 设置 lastMsg 作为拉取的起点,返回的消息列表中不包含这条消息。 3. 如果设置为空,则使用会话的最新一条消息作为拉取起点。 |
lastMsgSeq |
最后一条消息的 seq |
NO |
YES |
NO |
可用于拉取历史消息或消息定位等场景。 1. 仅能在群聊中使用该字段。 2. 设置 lastMsgSeq 作为拉取的起点,返回的消息列表中包含这条消息。 3. 如果同时指定了 lastMsg 和 lastMsgSeq,SDK 优先使用 lastMsg。 4. 如果均未指定 lastMsg 和 lastMsgSeq,拉取的起点取决于是否设置 getTimeBegin。设置了,则使用设置的范围作为起点;未设置,则使用最新消息作为起点。 |
getTimeBegin |
拉取消息的时间起点。UTC 时间戳,单位:秒 |
YES |
YES |
NO |
默认为 0,表示从当前时间开始拉取。 |
getTimePeriod |
拉取消息的时间范围。单位:秒 |
YES |
YES |
NO |
1. 默认为 0,表示不限制时间范围。 2. 取值的范围区间为闭区间,包含起止时间,二者关系如下: * 如果 getType 指定了朝消息时间更老的方向拉取,则时间范围表示为 [getTimeBegin - getTimePeriod, getTimeBegin]。 * 如果 getType 指定了朝消息时间更新的方向拉取,则时间范围表示为 [getTimeBegin, getTimeBegin + getTimePeriod]。 |
分页拉取
上述拉取单聊历史消息、拉取群聊历史消息,及高级接口均可按照相同的方式实现分页,即使用 lastMsg
和 count
来实现:
- 首次拉取,设置
lastMsg
为空,此时 SDK 会拉取到最新的消息。
- 非首次拉取,使用上一次拉取到的消息列表中最后一条消息作为本次拉取的
lastMsg
,拉取下一页数据。此时消息列表返回的消息不包含设置的 lastMsg。
- 拉取到的消息列表中,越新的消息越靠前。
说明:
考虑到加载效率、网络省流,建议分页时 count
设置为 20。
如果您使用 lastMsgSeq
拉取历史消息,返回的消息列表会包含该 lastMsgSeq
所对应的消息。
因此,非首次拉取群聊历史消息(也就是续拉)时,建议不要使用 lastMsgSeq
,否则可能出现同一条消息被重复拉到的情况。
例如,现在的历史消息有 8 条,分别是:msg1、msg2、msg3、msg4、msg5、msg6、msg7、msg8。
每次拉取 4 条,首次拉取得到了 msg1、msg2、msg3、msg4,此时再用 msg4 的 lastMsgSeq
续拉,会拉到消息 msg4、msg5、msg6、msg7。两次拉取中,msg4 被重复拉取了。
如果您一定要使用 lastMsgSeq
续拉,建议处理好消息去重的逻辑。
按照时间范围拉取
您可以通过设置 getTimeBegin
和 getTimePeriod
来拉取指定的时间范围内的消息。
如下图所示,时间范围的起止时间戳与 getType 有关。

示例代码将演示:从 2022-01-01 00:00:00 开始(时间戳是 1640966400),往过去的方向,从云端拉取一整天的群聊消息。
V2TIMMessageListGetOption option = new V2TIMMessageListGetOption();
option.setGetType(V2TIMMessageListGetOption.V2TIM_GET_CLOUD_OLDER_MSG); // 拉取云端的更老的消息
option.setGetTimeBegin(1640966400); // 从 2022-01-01 00:00:00 开始
option.setGetTimePeriod(1 * 24 * 60 * 60); // 拉取一整天的消息
option.setCount(Integer.MAX_VALUE); // 返回时间范围内所有的消息
option.setGroupID(#you group id#); // 拉取群聊消息
V2TIMManager.getMessageManager().getHistoryMessageList(option, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
option.getType = V2TIM_GET_CLOUD_OLDER_MSG;
option.getTimeBegin = 1640966400;
option.getTimePeriod = 1 * 24 * 60 * 60;
option.count = INT_MAX;
option.groupID = #your group id#;
[V2TIMManager.sharedInstance getHistoryMessageList:option succ:^(NSArray<V2TIMMessage *> *msgs) {
NSLog(@"success");
} fail:^(int code, NSString *desc) {
NSLog(@"failure, code:%d, desc:%@", code, desc);
}];
template <class T>
class ValueCallback final : public V2TIMValueCallback<T> {
public:
using SuccessCallback = std::function<void(const T&)>;
using ErrorCallback = std::function<void(int, const V2TIMString&)>;
ValueCallback() = default;
~ValueCallback() override = default;
void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
success_callback_ = std::move(success_callback);
error_callback_ = std::move(error_callback);
}
void OnSuccess(const T& value) override {
if (success_callback_) {
success_callback_(value);
}
}
void OnError(int error_code, const V2TIMString& error_message) override {
if (error_callback_) {
error_callback_(error_code, error_message);
}
}
private:
SuccessCallback success_callback_;
ErrorCallback error_callback_;
};
V2TIMMessageListGetOption option;
option.getType = V2TIMMessageGetType::V2TIM_GET_CLOUD_OLDER_MSG;
option.getTimeBegin = 1640966400;
option.getTimePeriod = 1 * 24 * 60 * 60;
option.count = std::numeric_limits<uint64_t>::max();
option.groupID = "your group id";
auto callback = new ValueCallback<V2TIMMessageVector>{};
callback->SetCallback(
[=](const V2TIMMessageVector& messageList) {
std::cout << "success" << std::endl;
delete callback;
},
[=](int error_code, const V2TIMString& error_message) {
std::cout << "error" << std::endl;
delete callback;
});
V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(option, callback);
在时间范围内分页拉取
您可以同时指定拉取的时间范围和 lastMsg
/lastMsgSeq
。此时 SDK 的表现是:
- 如果用户同时设置了
getTimeBegin
/getTimePeriod
和 lastMsg
/lastMsgSeq
,结果集可理解成:「单独按起始消息拉取的结果」与「单独按时间范围拉取的结果」 取交集。
- 如果用户都没设置
getTimeBegin
/getTimePeriod
和 lastMsg
/lastMsgSeq
,结果集可理解成:从当前会话最新的一条消息开始,按照 getType
所指定的方向和拉取方式拉取。
示例代码将演示:从 2022-01-01 00:00:00 开始(时间戳是 1640966400),往过去的方向,每页 20 条,分页地从云端拉取一整天的群聊消息。
// 定义变量,记录每次拉取的游标
private V2TIMMessage m_lastMsg = null; // 首次拉取时为 null
// 分页拉取逻辑
V2TIMMessageListGetOption option = new V2TIMMessageListGetOption();
option.setGetType(V2TIMMessageListGetOption.V2TIM_GET_CLOUD_OLDER_MSG); // 拉取云端的更老的消息
option.setGetTimeBegin(1640966400); // 从 2022-01-01 00:00:00 开始
option.setGetTimePeriod(1 * 24 * 60 * 60); // 拉取一整天的消息
option.setCount(20); // 每页 20 条
option.setLastMsg(m_lastMsg); // 上次拉取的位置 (每次返回的消息列表的最后一条消息)
option.setGroupID(#you group id#); // 拉取群聊消息
V2TIMManager.getMessageManager().getHistoryMessageList(option, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
Log.i("imsdk", "success");
// 记录下一次拉取的 lastMsg
m_lastMsg = v2TIMMessages.get(v2TIMMessages.size() - 1);
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
@property (nonatomic, copy) V2TIMMessage *lastMsg;
V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
option.getType = V2TIM_GET_CLOUD_OLDER_MSG;
option.getTimeBegin = 1640966400;
option.getTimePeriod = 1 * 24 * 60 * 60;
option.count = 20;
option.lastMsg = self.lastMsg;
option.groupID = #your group id#;
__weak typeof(self) weakSelf = self;
[V2TIMManager.sharedInstance getHistoryMessageList:option succ:^(NSArray<V2TIMMessage *> *msgs) {
NSLog(@"success");
weakSelf.lastMsg = msgs.lastObject;
} fail:^(int code, NSString *desc) {
NSLog(@"failure, code:%d, desc:%@", code, desc);
}];
template <class T>
class ValueCallback final : public V2TIMValueCallback<T> {
public:
using SuccessCallback = std::function<void(const T&)>;
using ErrorCallback = std::function<void(int, const V2TIMString&)>;
ValueCallback() = default;
~ValueCallback() override = default;
void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
success_callback_ = std::move(success_callback);
error_callback_ = std::move(error_callback);
}
void OnSuccess(const T& value) override {
if (success_callback_) {
success_callback_(value);
}
}
void OnError(int error_code, const V2TIMString& error_message) override {
if (error_callback_) {
error_callback_(error_code, error_message);
}
}
private:
SuccessCallback success_callback_;
ErrorCallback error_callback_;
};
V2TIMMessage lastMsg;
V2TIMMessageListGetOption option;
option.getType = V2TIMMessageGetType::V2TIM_GET_CLOUD_OLDER_MSG;
option.getTimeBegin = 1640966400;
option.getTimePeriod = 1 * 24 * 60 * 60;
option.count = 20;
option.lastMsg = 首次拉取 ? nullptr : &lastMsg;
option.groupID = "your group id";
auto callback = new ValueCallback<V2TIMMessageVector>{};
callback->SetCallback(
[=](const V2TIMMessageVector& messageList) mutable {
std::cout << "success" << std::endl;
lastMsg = messageList[messageList.Size() - 1];
delete callback;
},
[=](int error_code, const V2TIMString& error_message) {
std::cout << "error" << std::endl;
delete callback;
});
V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(option, callback);
仅拉取本地消息
通过设置 getType
来实现仅拉取本地消息:
getType
取值为 V2TIM_GET_LOCAL_OLDER_MSG
时,表示往时间更旧的方向,拉取本地存储的消息。
getType
取值为 V2TIM_GET_LOCAL_NEWER_MSG
时,表示往时间更新的方向,拉取本地存储的消息。
示例代码将演示:从最新的消息开始,往更老的方向,从本地数据库中拉取 20 条单聊消息。
V2TIMMessageListGetOption option = new V2TIMMessageListGetOption();
option.setGetType(V2TIMMessageListGetOption.V2TIM_GET_LOCAL_OLDER_MSG); // 拉取本地的更老的消息
option.setLastMsg(null); // 设置从最新的消息开始拉取
option.setCount(20); // 拉取 20 条消息
option.setUserID(#you user id#); // 拉取单聊消息
V2TIMManager.getMessageManager().getHistoryMessageList(option, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
option.getType = V2TIM_GET_LOCAL_OLDER_MSG;
option.lastMsg = nil;
option.count = 20;
option.userID = #your user id#;
[V2TIMManager.sharedInstance getHistoryMessageList:option succ:^(NSArray<V2TIMMessage *> *msgs) {
NSLog(@"success");
} fail:^(int code, NSString *desc) {
NSLog(@"failure, code:%d, desc:%@", code, desc);
}];
template <class T>
class ValueCallback final : public V2TIMValueCallback<T> {
public:
using SuccessCallback = std::function<void(const T&)>;
using ErrorCallback = std::function<void(int, const V2TIMString&)>;
ValueCallback() = default;
~ValueCallback() override = default;
void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
success_callback_ = std::move(success_callback);
error_callback_ = std::move(error_callback);
}
void OnSuccess(const T& value) override {
if (success_callback_) {
success_callback_(value);
}
}
void OnError(int error_code, const V2TIMString& error_message) override {
if (error_callback_) {
error_callback_(error_code, error_message);
}
}
private:
SuccessCallback success_callback_;
ErrorCallback error_callback_;
};
V2TIMMessageListGetOption option;
option.getType = V2TIMMessageGetType::V2TIM_GET_LOCAL_OLDER_MSG;
option.count = 20;
option.userID = "you user id";
auto callback = new ValueCallback<V2TIMMessageVector>{};
callback->SetCallback(
[=](const V2TIMMessageVector& messageList) mutable {
std::cout << "success" << std::endl;
delete callback;
},
[=](int error_code, const V2TIMString& error_message) {
std::cout << "error" << std::endl;
delete callback;
});
V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(option, callback);
拉取指定类型的消息
SDK 定义一些常见的消息类型,例如文本、图片、视频等,参考 V2TIMElemType
(Android / iOS & Mac / Windows)。高级接口 getHistoryMessageList
支持通过设置 messageTypeList
,拉取指定类型的消息。
注意:
- 当
messageTypeList
为空时,表示拉取所有类型的消息。
- 拉取指定的消息类型,只支持从本地拉取。从云端拉取消息不支持指定消息类型。
示例代码将演示:从当前开始,往过去的方向,拉取 20 条文本和图片消息。
ArrayList<Integer> messageTypeList = new ArrayList<Integer>();
messageTypeList.add(V2TIM_ELEM_TYPE_IMAGE);
messageTypeList.add(V2TIM_ELEM_TYPE_TEXT);
V2TIMMessageListGetOption option = new V2TIMMessageListGetOption();
option.setGetType(V2TIMMessageListGetOption.V2TIM_GET_CLOUD_OLDER_MSG);
option.setCount(20);
option.setMessageTypeList(messageTypeList);
option.setGroupID("you group id");
V2TIMManager.getMessageManager().getHistoryMessageList(option, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
option.getType = V2TIM_GET_CLOUD_OLDER_MSG;
option.messageTypeList = @[@(V2TIM_ELEM_TYPE_IMAGE), @(V2TIM_ELEM_TYPE_TEXT)];
option.count = 20;
option.groupID = @"your group id";
[V2TIMManager.sharedInstance getHistoryMessageList:option succ:^(NSArray<V2TIMMessage *> *msgs) {
NSLog(@"success");
} fail:^(int code, NSString *desc) {
NSLog(@"failure, code:%d, desc:%@", code, desc);
}];
template <class T>
class ValueCallback final : public V2TIMValueCallback<T> {
public:
using SuccessCallback = std::function<void(const T&)>;
using ErrorCallback = std::function<void(int, const V2TIMString&)>;
ValueCallback() = default;
~ValueCallback() override = default;
void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
success_callback_ = std::move(success_callback);
error_callback_ = std::move(error_callback);
}
void OnSuccess(const T& value) override {
if (success_callback_) {
success_callback_(value);
}
}
void OnError(int error_code, const V2TIMString& error_message) override {
if (error_callback_) {
error_callback_(error_code, error_message);
}
}
private:
SuccessCallback success_callback_;
ErrorCallback error_callback_;
};
V2TIMMessageListGetOption option;
option.getType = V2TIMMessageGetType::V2TIM_GET_CLOUD_OLDER_MSG;
option.count = 20;
option.messageTypeList.PushBack(V2TIMElemType::V2TIM_ELEM_TYPE_IMAGE);
option.messageTypeList.PushBack(V2TIMElemType::V2TIM_ELEM_TYPE_TEXT);
option.userID = "you group id";
auto callback = new ValueCallback<V2TIMMessageVector>{};
callback->SetCallback(
[=](const V2TIMMessageVector& messageList) mutable {
std::cout << "success" << std::endl;
delete callback;
},
[=](int error_code, const V2TIMString& error_message) {
std::cout << "error" << std::endl;
delete callback;
});
V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(option, callback);
一键跳转到群 @ 消息
在群聊会话中,收到群 @ 消息后,一般需要通过点击群 @ 提示条,跳转到群 @ 消息的位置,并拉取附近的消息列表用于显示。
由于群 @ 消息本身也需要显示,可以将 lastMsgSeq 设置成群 @ 消息的 sequence
,并使用高级接口 getHistoryMessageList
来拉取。
示例代码将演示:点击群 @ 提示后,跳转到群 @ 消息,并拉取前后各 20 条消息用于展示。
// 获取群 @ 消息对应的的 sequence
long atSequence = 1081;
// 拉取群 @ 消息及之前的消息
V2TIMMessageListGetOption beforeOption = new V2TIMMessageListGetOption();
beforeOption.setGetType(V2TIMMessageListGetOption.V2TIM_GET_CLOUD_OLDER_MSG); // 拉取比群 @ 消息更早的消息
beforeOption.setCount(20); // 拉取 20 条
beforeOption.setLastMsgSeq(atSequence); // 从群 @ 消息开始拉取,包括群 @ 消息
beforeOption.setGroupID(#you group id#); // 拉取群聊消息
V2TIMManager.getMessageManager().getHistoryMessageList(beforeOption, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {、
// 返回的消息列表中包括群 @ 消息
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
// 拉取群 @ 消息之后的消息
V2TIMMessageListGetOption afterOption = new V2TIMMessageListGetOption();
afterOption.setGetType(V2TIMMessageListGetOption.V2TIM_GET_CLOUD_NEWER_MSG); // 拉取比群 @ 消息更晚的消息
afterOption.setCount(20); // 拉取 20 条
afterOption.setLastMsgSeq(atSequence + 1); // 从群 @ 消息的后一条消息开始拉取,不包括群 @ 消息
afterOption.setGroupID(#you group id#); // 拉取群聊消息
V2TIMManager.getMessageManager().getHistoryMessageList(afterOption, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
// 返回的消息列表中不包括群 @ 消息
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
NSInteger atSequence = 1081;
V2TIMMessageListGetOption *beforeOption = [[V2TIMMessageListGetOption alloc] init];
beforeOption.getType = V2TIM_GET_CLOUD_OLDER_MSG;
beforeOption.count = 20;
beforeOption.lastMsgSeq = atSequence;
beforeOption.groupID = #your group id#;
[V2TIMManager.sharedInstance getHistoryMessageList:beforeOption succ:^(NSArray<V2TIMMessage *> *msgs) {
NSLog(@"success");
} fail:^(int code, NSString *desc) {
NSLog(@"failure, code:%d, desc:%@", code, desc);
}];
V2TIMMessageListGetOption *afterOption = [[V2TIMMessageListGetOption alloc] init];
afterOption.getType = V2TIM_GET_CLOUD_NEWER_MSG;
afterOption.count = 20;
afterOption.lastMsgSeq = atSequence + 1;
afterOption.groupID = #your group id#;
[V2TIMManager.sharedInstance getHistoryMessageList:afterOption succ:^(NSArray<V2TIMMessage *> *msgs) {
NSLog(@"success");
} fail:^(int code, NSString *desc) {
NSLog(@"failure, code:%d, desc:%@", code, desc);
}];
template <class T>
class ValueCallback final : public V2TIMValueCallback<T> {
public:
using SuccessCallback = std::function<void(const T&)>;
using ErrorCallback = std::function<void(int, const V2TIMString&)>;
ValueCallback() = default;
~ValueCallback() override = default;
void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
success_callback_ = std::move(success_callback);
error_callback_ = std::move(error_callback);
}
void OnSuccess(const T& value) override {
if (success_callback_) {
success_callback_(value);
}
}
void OnError(int error_code, const V2TIMString& error_message) override {
if (error_callback_) {
error_callback_(error_code, error_message);
}
}
private:
SuccessCallback success_callback_;
ErrorCallback error_callback_;
};
uint64_t atSequence = 1081;
V2TIMMessageListGetOption beforeOption;
beforeOption.getType = V2TIMMessageGetType::V2TIM_GET_CLOUD_OLDER_MSG;
beforeOption.count = 20;
beforeOption.lastMsgSeq = atSequence;
beforeOption.userID = "you group id";
auto beforeCallback = new ValueCallback<V2TIMMessageVector>{};
beforeCallback->SetCallback(
[=](const V2TIMMessageVector& messageList) mutable {
std::cout << "success" << std::endl;
delete beforeCallback;
},
[=](int error_code, const V2TIMString& error_message) {
std::cout << "error" << std::endl;
delete beforeCallback;
});
V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(beforeOption, beforeCallback);
V2TIMMessageListGetOption afterOption;
afterOption.getType = V2TIMMessageGetType::V2TIM_GET_CLOUD_NEWER_MSG;
afterOption.count = 20;
afterOption.lastMsgSeq = atSequence + 1;
afterOption.userID = "you group id";
auto afterCallback = new ValueCallback<V2TIMMessageVector>{};
afterCallback->SetCallback(
[=](const V2TIMMessageVector& messageList) mutable {
std::cout << "success" << std::endl;
delete afterCallback;
},
[=](int error_code, const V2TIMString& error_message) {
std::cout << "error" << std::endl;
delete afterCallback;
});
V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(afterOption, afterCallback);
搜索场景下的消息定位
当用户通过本地搜索获取到 V2TIMMessage
对象。点击该消息后,一般需要跳转到该消息对应的位置并展示附近的消息。
此时可以将 lastMsg
设置成该消息对象,并使用高级接口 getHistoryMessageList
拉取前后的消息。
示例代码将演示:拉取搜索到的消息前后各 20 条消息,用于展示。
// 获取当前的 message 对象
V2TIMMessage lastMsg = #搜索到的消息#;
// 拉取指定消息之前的消息
V2TIMMessageListGetOption beforeOption = new V2TIMMessageListGetOption();
beforeOption.setGetType(V2TIMMessageListGetOption.V2TIM_GET_CLOUD_OLDER_MSG); // 拉取比指定消息更早的消息
beforeOption.setCount(20); // 拉取 20 条
beforeOption.setLastMsg(lastMsg); // 从指定消息开始拉取,不包括该消息
beforeOption.setGroupID(#you group id#); // 拉取群聊消息
V2TIMManager.getMessageManager().getHistoryMessageList(beforeOption, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {、
// 返回的消息列表中不包括 lastMsg
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
// 拉取指定消息之后的消息
V2TIMMessageListGetOption afterOption = new V2TIMMessageListGetOption();
afterOption.setGetType(V2TIMMessageListGetOption.V2TIM_GET_CLOUD_NEWER_MSG); // 拉取比指定消息更晚的消息
afterOption.setCount(20); // 拉取 20 条
afterOption.setLastMsg(lastMsg); // 从指定消息开始拉取,不包括该消息
afterOption.setGroupID(#you group id#); // 拉取群聊消息
V2TIMManager.getMessageManager().getHistoryMessageList(afterOption, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
// 返回的消息列表中不包括 lastMsg
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
V2TIMMessage *lastMsg = #搜索后的消息#;
V2TIMMessageListGetOption *beforeOption = [[V2TIMMessageListGetOption alloc] init];
beforeOption.getType = V2TIM_GET_CLOUD_OLDER_MSG;
beforeOption.count = 20;
beforeOption.lastMsg = lastMsg;
beforeOption.groupID = #your group id#;
[V2TIMManager.sharedInstance getHistoryMessageList:beforeOption succ:^(NSArray<V2TIMMessage *> *msgs) {
NSLog(@"success");
} fail:^(int code, NSString *desc) {
NSLog(@"failure, code:%d, desc:%@", code, desc);
}];
V2TIMMessageListGetOption *afterOption = [[V2TIMMessageListGetOption alloc] init];
afterOption.getType = V2TIM_GET_CLOUD_NEWER_MSG;
afterOption.count = 20;
afterOption.lastMsg = lastMsg;
afterOption.groupID = #your group id#;
[V2TIMManager.sharedInstance getHistoryMessageList:afterOption succ:^(NSArray<V2TIMMessage *> *msgs) {
NSLog(@"success");
} fail:^(int code, NSString *desc) {
NSLog(@"failure, code:%d, desc:%@", code, desc);
}];
template <class T>
class ValueCallback final : public V2TIMValueCallback<T> {
public:
using SuccessCallback = std::function<void(const T&)>;
using ErrorCallback = std::function<void(int, const V2TIMString&)>;
ValueCallback() = default;
~ValueCallback() override = default;
void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
success_callback_ = std::move(success_callback);
error_callback_ = std::move(error_callback);
}
void OnSuccess(const T& value) override {
if (success_callback_) {
success_callback_(value);
}
}
void OnError(int error_code, const V2TIMString& error_message) override {
if (error_callback_) {
error_callback_(error_code, error_message);
}
}
private:
SuccessCallback success_callback_;
ErrorCallback error_callback_;
};
V2TIMMessage lastMsg = 搜索到的消息;
V2TIMMessageListGetOption beforeOption;
beforeOption.getType = V2TIMMessageGetType::V2TIM_GET_CLOUD_OLDER_MSG;
beforeOption.count = 20;
beforeOption.lastMsg = lastMsg;
beforeOption.userID = "you group id";
auto beforeCallback = new ValueCallback<V2TIMMessageVector>{};
beforeCallback->SetCallback(
[=](const V2TIMMessageVector& messageList) mutable {
std::cout << "success" << std::endl;
delete beforeCallback;
},
[=](int error_code, const V2TIMString& error_message) {
std::cout << "error" << std::endl;
delete beforeCallback;
});
V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(beforeOption, beforeCallback);
V2TIMMessageListGetOption afterOption;
afterOption.getType = V2TIMMessageGetType::V2TIM_GET_CLOUD_NEWER_MSG;
afterOption.count = 20;
afterOption.lastMsg = lastMsg;
afterOption.userID = "you group id";
auto afterCallback = new ValueCallback<V2TIMMessageVector>{};
afterCallback->SetCallback(
[=](const V2TIMMessageVector& messageList) mutable {
std::cout << "success" << std::endl;
delete afterCallback;
},
[=](int error_code, const V2TIMString& error_message) {
std::cout << "error" << std::endl;
delete afterCallback;
});
V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(afterOption, afterCallback);
解析信令消息
如果您使用了 IM SDK 的信令相关接口(Android / iOS & Mac),调用 invite/inviteInGroup
时,设置了 onlineUserOnly
为 false
(Android) / NO
(iOS),那么每次信令操作(包括 invite
、cancel
、accept
、reject
、timeout
)都会产生一条自定义消息,该消息会通过 V2TIMAdvancedMsgListener
的 onRecvNewMessage
回调抛给用户,也可以通过历史消息拉取。
如果您需要根据信令信息做自定义化文本展示,可以使用信令解析接口 getSignalingInfo
(Android / iOS & Mac / Windows) 获取信令信息。
接口原型
public abstract V2TIMSignalingInfo getSignalingInfo(V2TIMMessage msg);
- (V2TIMSignalingInfo *)getSignallingInfo:(V2TIMMessage *)msg;
virtual V2TIMSignalingInfo GetSignalingInfo(const V2TIMMessage& msg) = 0;
示例代码将演示:拉取到历史消息之后,解析信令消息的显示文本。
public String getCallSignalingContentWithMessage(V2TIMMessage message) {
if (message == null) {
Log.e(TAG, " invalid param ");
return "";
}
V2TIMSignalingInfo signalingInfo = V2TIMManager.getSignalingManager().getSignalingInfo(message);
if (signalingInfo == null) {
Log.e(TAG, " signalingInfo is null ");
return "";
}
String content = "";
Gson gson = new Gson();
Object businessIdObj = null;
String businessId = null;
try {
HashMap signalDataMap = gson.fromJson(signalingInfo.getData(), HashMap.class);
if (signalDataMap != null) {
businessIdObj = signalDataMap.get(TUIConstants.Message.CUSTOM_BUSINESS_ID_KEY);
} else {
Log.e(TAG, " signalDataMap is null ");
return "";
}
} catch (JsonSyntaxException e) {
Log.e(TAG, " get signalingInfoCustomJsonMap error ");
}
if (businessIdObj instanceof String) {
businessId = (String) businessIdObj;
}
if (TextUtils.equals(businessId, "av_call")) {
if (signalingInfo.getActionType() == V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_INVITE) {
content = "@邀请通话";
} else if (signalingInfo.getActionType() == V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_ACCEPT_INVITE) {
content = "@同意通话";
} else {
}
}
return content;
}
+ (NSString *)getCallSignalingContentWithMessage:(V2TIMMessage *)message
{
V2TIMSignalingInfo *info = [[V2TIMManager sharedInstance] getSignallingInfo:message];
if (!info) {
return nil;
}
NSError *err = nil;
NSDictionary *param = nil;
if (info.data != nil) {
param = [NSJSONSerialization JSONObjectWithData:[info.data dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&err];
}
if (!param || ![param isKindOfClass:[NSDictionary class]]) {
return nil;
}
NSArray *allKeys = param.allKeys;
if (![allKeys containsObject:@"businessID"]) {
return nil;
}
NSString *businessId = [param objectForKey:@"businessID"];
NSString *content = nil;
if ([businessId isEqualToString:@"av_call"]) {
if (info.actionType == SignalingActionType_Invite) {
content = @"邀请通话";
} else if (info.actionType == SignalingActionType_Accept_Invite) {
content = @"同意通话";
} else {
}
}
return content;
}
信令消息对应的自定义文本
std::string GetCallSignalingContentWithMessage(const V2TIMMessage& message) {
V2TIMSignalingInfo signalingInfo =
V2TIMManager::GetInstance()->GetSignalingManager()->GetSignalingInfo(message);
if (signalingInfo.inviteID.Empty()) {
return {};
}
rapidjson::Document document;
document.Parse(signalingInfo.data.CString(), signalingInfo.data.Size());
if (!document.HasMember("businessId") || !document["businessId"].IsString()) {
return {};
}
const char* businessId = document["businessId"].GetString();
std::string content;
if (businessId == "av_call") {
if (signalingInfo.actionType == V2TIMSignalingActionType::SignalingActionType_Invite) {
content = u8"邀请通话";
} else if (signalingInfo.actionType == 2TIMSignalingActionType::SignalingActionType_Accept_Invite) {
content = u8"同意通话";
} else {
}
}
return content;
}
常见问题
1. 拉历史消息时,日志中出现 "total count of request cloud message exceed max limit" 信息
SDK 目前的策略是:
- 当
getType
设置成拉云端历史消息,且拉取 count 条消息时,SDK 会从云端拉取 count 条消息。
- SDK 过滤无效的消息,例如消息被删除、非当前用户关心的消息等。
- 当云端历史消息中无效的消息过多,会触发 SDK 多次分页拉取。
为了提供系统的稳定性和健壮性,SDK 最多触发 3 次自动分页。当超过限制后,会出现 “total count of request cloud message exceed max limit” 的日志信息。
为了尽可能的减少此类限频机制对业务层的影响,您可以使用如下措施来减少无效消息的产生:
- 您可以使用在线消息,即发送消息时设置
onlineUserOnly
为 YES/true
。
- 如果是群聊消息,可以使用群定向消息指定消息的接收者,避免产生无效消息。
2. 拉云端历史消息时,消息 “丢失” ?
当 getType
设置成拉云端历史消息,且拉取 count 条消息时,SDK 会做如下操作:
- SDK 先从本地拉取 count 条消息。
- SDK 再次云端拉取 count 条消息,过滤掉被删除等无效的消息,如果不够 count 条,SDK 内部触发分页拉取。
- 将本地和云端消息进行合并,更新消息状态等信息。
- 从合并的消息列表中,返回 count 条消息。
一般出现消息 “丢失” 时,指的是在第 2 步中拉取的无效消息过多,导致触发了问题 1 中的限频机制,从而导致实际拉取的云端消息不够。
建议按照问题 1 中的解决方法来处理,如果仍然无法解决,欢迎加入文末的 QQ 群反馈。
3. 拉取的历史消息,群名片等群成员信息没有实时更新?
- SDK 会在消息产生时,更新当前的群名片、role 等群成员信息并存储在本地数据库中。
- 当拉取群历史消息时,会直接查询消息产生时的群成员信息,不会实时向后台请求更新。
如果您需要获取最新的群成员信息,您可以使用 getGroupMembersInfo
(Android / iOS & Mac / Windows)。
4. 拉取历史消息时卡顿
SDK 内部已对消息拉取做了性能优化,您如果碰到消息卡顿的情况,可以先尝试减少拉取的消息数 count
,如果还是不能解决问题,欢迎加入文末的 QQ 群反馈。
本页内容是否解决了您的问题?