功能描述
流式消息(Stream Message)是即时通信 IM SDK 提供的一种特殊消息类型,专为 AI 对话等场景设计。在这类场景中,消息内容由服务端逐步生成,SDK 会实时接收内容分片并累加,直到整条消息生成完毕。客户端通过监听消息修改回调,即可实时获取流式消息的最新内容,实现类似 ChatGPT 的逐字输出效果。
说明:
流式消息功能在 8.9 及以上版本支持。
SDK 内部自动完成流拉取、分片排序、ACK 确认等操作,客户端无需手动调用额外接口。
流式消息通过
onRecvMessageModified 回调持续通知 UI 层更新内容。接口说明
V2TIMStreamElem 属性
流式消息的 Elem 类型为
V2TIM_ELEM_TYPE_STREAM(值为 11),对应的 Elem 类为 V2TIMStreamElem。V2TIMStreamElem 属性说明如下:属性 | 含义 | 说明 |
markdown | 流式消息文本内容 | 随着分片到达逐步累加的文本内容,只读。 |
data | 流式消息二进制数据 | 随着分片到达逐步累加的二进制数据,只读。 |
isStreamEnded | 流式消息是否结束 | 为 YES/true 时表示服务端已完成所有内容推送。 |
说明:
markdown 和 data 均为只读属性,其内容由 SDK 内部自动维护和累加,客户端只需读取即可。每次
onRecvMessageModified 回调中获取到的 markdown / data 已是累加后的完整内容,无需客户端自行拼接。添加高级消息监听
流式消息通过高级消息监听器
V2TIMAdvancedMsgListener 接收和更新,需要事先调用 addAdvancedMsgListener 添加监听。示例代码如下:
V2TIMManager.getMessageManager().addAdvancedMsgListener(new V2TIMAdvancedMsgListener() {@Overridepublic void onRecvNewMessage(V2TIMMessage msg) {// 收到新消息}@Overridepublic void onRecvMessageModified(V2TIMMessage msg) {// 消息内容被修改(流式消息内容更新时触发)}});
V2TIMManager.shared.addAdvancedMsgListener(listener: self)
[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(this);
接收流式消息
当收到一条新的流式消息时,会通过
onRecvNewMessage 回调通知。此时消息中的 streamElem 内容可能还不完整(isStreamEnded 为 NO/false)。示例代码如下:
@Overridepublic void onRecvNewMessage(V2TIMMessage msg) {if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_STREAM) {V2TIMStreamElem streamElem = msg.getStreamElem();// 在 UI 上展示消息,后续内容会通过 onRecvMessageModified 持续更新}}
func onRecvNewMessage(_ msg: V2TIMMessage) {if msg.elemType == .ELEM_TYPE_STREAM {if let streamElem = msg.streamElem {// 在 UI 上展示消息,后续内容会通过 onRecvMessageModified 持续更新}}}
- (void)onRecvNewMessage:(V2TIMMessage *)msg {if (msg.elemType == V2TIM_ELEM_TYPE_STREAM) {V2TIMStreamElem *streamElem = msg.streamElem;// 在 UI 上展示消息,后续内容会通过 onRecvMessageModified 持续更新}}
void OnRecvNewMessage(const V2TIMMessage &message) override {if (message.elemList.Size() > 0) {V2TIMElem *elem = message.elemList[0];if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_STREAM) {auto *streamElem = static_cast<V2TIMStreamElem *>(elem);// 在 UI 上展示消息,后续内容会通过 OnRecvMessageModified 持续更新}}}
监听流式消息内容更新
SDK 收到新的分片数据后,会自动将内容累加到
streamElem 中,并通过 onRecvMessageModified 回调通知上层。客户端只需在该回调中读取最新的 markdown / data 内容并刷新 UI 即可。说明:
onRecvMessageModified 回调可能触发多次,每收到一批有效分片都会触发一次,请确保 UI 刷新逻辑高效。当
isStreamEnded 为 YES/true 时,表示流式消息已结束,可以停止加载动画等 UI 状态。示例代码如下:
@Overridepublic void onRecvMessageModified(V2TIMMessage msg) {if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_STREAM) {V2TIMStreamElem streamElem = msg.getStreamElem();// 更新 UI 上展示的内容updateMessageUI(msg.getMsgID(), streamElem.getMarkdown());if (streamElem.isStreamEnded()) {// 流式消息已结束,可以做最终处理}}}
func onRecvMessageModified(_ msg: V2TIMMessage) {if msg.elemType == .ELEM_TYPE_STREAM {if let streamElem = msg.streamElem {// 更新 UI 上展示的内容updateMessageUI(msgID: msg.msgID, content: streamElem.markdown)if streamElem.isStreamEnded {// 流式消息已结束,可以做最终处理}}}}
- (void)onRecvMessageModified:(V2TIMMessage *)msg {if (msg.elemType == V2TIM_ELEM_TYPE_STREAM) {V2TIMStreamElem *streamElem = msg.streamElem;// 更新 UI 上展示的内容[self updateMessageUI:msg.msgID withContent:streamElem.markdown];if (streamElem.isStreamEnded) {// 流式消息已结束,可以做最终处理}}}
void OnRecvMessageModified(const V2TIMMessage &message) override {if (message.elemList.Size() > 0) {V2TIMElem *elem = message.elemList[0];if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_STREAM) {auto *streamElem = static_cast<V2TIMStreamElem *>(elem);// 更新 UI 上展示的内容// streamElem->markdown 为当前已累加的完整文本if (streamElem->isStreamEnded) {// 流式消息已结束,可以做最终处理}}}}
历史消息中的流式消息
当拉取历史消息时:
若消息中包含未完成的流式消息,SDK 会自动恢复拉流操作,并通过
onRecvMessageModified 回调继续更新内容,客户端无需额外处理。若消息中的流式消息已全部拉取完毕,拉取历史消息后可直接展示完整内容,且不会再次触发
onRecvMessageModified 回调。