即时通信 IM Android&iOS&Windows&Mac(接收消息)

SDK 提供了 2 种消息监听器,简单消息监听器 V2TIMSimpleMsgListener 和高级消息监听器 V2TIMAdvancedMsgListener。
两者的区别在于。

相关产品
即时通信 IM 产品页,含版本规格、价格与购买入口。

一、功能描述

·通过 addSimpleMsgListener 监听接收文本、自定义消息,相关回调在 V2TIMSimpleMsgListener 协议中定义。

·通过 addAdvancedMsgListener 监听接收所有类型消息(文本、自定义、富媒体消息),相关回调在 V2TIMAdvancedMsgListener 协议中定义。

二、设置消息监听器

1. 简单消息监听器只能接收文本、自定义消息。如果你的业务只需要这两种消息,可以仅使用简单消息监听器。

2. 高级消息监听器可以接收所有类型的消息。如果你的业务还需要支持富媒体、合并消息等其他类型,请使用高级消息监听器。

注意:1. addSimpleMsgListener 和 addAdvancedMsgListener 请使用其中之一,切勿混用,以免产生不可预知的逻辑 bug。

注意:2. 如果想要正常接收下面各种类型的消息,必须先添加消息监听器,否则无法正常接收。

四、添加监听器

接收方调用 addSimpleMsgListener (Java / Swift / Objective-C / C++) 添加简单消息监听器。一般建议在比较靠前的时间点调用,例如聊天消息界面初始化后,确保 App 能及时收到消息。

示例代码如下:

JavaSwiftObjective-CC++V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);V2TIMManager.shared.addSimpleMsgListener(listener: self)// self 为 id<V2TIMSignalingListener>[[V2TIMManager sharedInstance] addSimpleMsgListener:self];class SimpleMsgListener final : public V2TIMSimpleMsgListener { // 成员 …};// 添加基本消息的事件监听器,注意在移除监听器之前需要保持 simpleMsgListener 的生命期,以免接收不到事件回调SimpleMsgListener simpleMsgListener;V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

五、监听器回调事件

添加成功简单消息监听器后,接收方可以在 V2TIMSimpleMsgListener (Java / Swift / Objective-C / C++) 的回调中接收不同类型消息,说明如下:

JavaSwiftObjective-CC++public abstract class V2TIMSimpleMsgListener { // 收到 C2C 文本消息 public void onRecvC2CTextMessage(String msgID, V2TIMUserInfo sender, String text) {} // 收到 C2C 自定义(信令)消息 public void onRecvC2CCustomMessage(String msgID, V2TIMUserInfo sender, byte[] customData) {} // 收到群文本消息 public void onRecvGroupTextMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, String text) {} // 收到群自定义(信令)消息 public void onRecvGroupCustomMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, byte[] customData) {}}public protocol V2TIMSimpleMsgListener : AnyObject { /// 收到 C2C 文本消息 func onRecvC2CTextMessage(msgID: String, sender: V2TIMUserInfo, text: String?) /// 收到 C2C 自定义(信令)消息 func onRecvC2CCustomMessage(msgID: String, sender: V2TIMUserInfo, customData: Data?) /// 收到群文本消息 func onRecvGroupTextMessage(msgID: String, groupID: String, sender: V2TIMGroupMemberInfo, text: String?) /// 收到群自定义(信令)消息 func onRecvGroupCustomMessage(msgID: String, groupID: String, sender: V2TIMGroupMemberInfo, customData: Data?)}/// IMSDK 基本消息回调@protocol V2TIMSimpleMsgListener <NSObject>@optional/// 收到 C2C 文本消息- (void)onRecvC2CTextMessage:(NSString *)msgID sender:(V2TIMUserInfo *)info text:(NSString *)text;/// 收到 C2C 自定义(信令)消息- (void)onRecvC2CCustomMessage:(NSString *)msgID sender:(V2TIMUserInfo *)info customData:(NSData *)data;/// 收到群文本消息- (void)onRecvGroupTextMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info text:(NSString *)text;/// 收到群自定义(信令)消息- (void)onRecvGroupCustomMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info customData:(NSData *)data;@endclass SimpleMsgListener final : public V2TIMSimpleMsgListener {public: SimpleMsgListener() = default; ~SimpleMsgListener() override = default; // 收到 C2C 文本消息 void OnRecvC2CTextMessage(const V2TIMString& msgID, const V2TIMUserFullInfo& sender, const V2TIMString& text) override {} // 收到 C2C 自定义(信令)消息 void OnRecvC2CCustomMessage(const V2TIMString& msgID, const V2TIMUserFullInfo& sender, const V2TIMBuffer& customData) override {} // 收到群文本消息 void OnRecvGroupTextMessage(const V2TIMString& msgID, const V2TIMString& groupID, const V2TIMGroupMemberFullInfo& sender, const V2TIMString& text) override {} // 收到群自定义(信令)消息 void OnRecvGroupCustomMessage(const V2TIMString& msgID, const V2TIMString& groupID, const V2TIMGroupMemberFullInfo& sender, const V2TIMBuffer& customData) override {}};

六、移除监听器

如果想停止接收消息,接收方可调用 removeSimpleMsgListener (Java / Swift / Objective-C / C++) 移除简单消息监听器。

示例代码如下:

JavaSwiftObjective-CC++V2TIMManager.getInstance().removeSimpleMsgListener(simpleMsgListener);V2TIMManager.shared.removeSimpleMsgListener(listener: self)// self 为 id<V2TIMSignalingListener>[[V2TIMManager sharedInstance] removeSimpleMsgListener:self];class SimpleMsgListener final : public V2TIMSimpleMsgListener { // 成员 …};// simpleMsgListener 是 SimpleMsgListener 的实例V2TIMManager::GetInstance()->RemoveSimpleMsgListener(&simpleMsgListener);

八、添加监听器

接收方调用 addAdvancedMsgListener (Java / Swift / Objective-C / C++) 添加高级消息监听器。一般建议在比较靠前的时间点调用,例如聊天消息界面初始化后,确保 App 能及时收到消息。

示例代码如下:

JavaSwiftObjective-CC++V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener);V2TIMManager.shared.addAdvancedMsgListener(listener: self)// self 为 id<V2TIMAdvancedMsgListener>[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];class AdvancedMsgListener final : public V2TIMAdvancedMsgListener { // 成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

九、监听器回调事件

添加成功高级消息监听器后,接收方可以在 V2TIMAdvancedMsgListener (Java / Swift / Objective-C / C++) 的回调中接收不同类型消息,说明如下:

JavaSwiftObjective-CC++public abstract class V2TIMAdvancedMsgListener { // 收到新消息 public void onRecvNewMessage(V2TIMMessage msg) {} // C2C 对端用户会话已读通知(对端用户调用 markC2CMessageAsRead,自己会收到该通知) public void onRecvC2CReadReceipt(List<V2TIMMessageReceipt> receiptList) {} // 消息已读回执通知(如果自己发送的消息支持已读回执,消息接收端调用 sendMessageReadReceipts,自己会收到该通知) public void onRecvMessageReadReceipts(List<V2TIMMessageReceipt> receiptList) {} // 收到消息撤回的通知 public void onRecvMessageRevoked(String msgID) {} // 消息内容被修改 public void onRecvMessageModified(V2TIMMessage msg) {}}public protocol V2TIMAdvancedMsgListener: AnyObject { /// 收到新消息 func onRecvNewMessage(msg: V2TIMMessage) /// 消息已读回执通知(如果自己发的消息支持已读回执,消息接收端调用了 sendMessageReadReceipts 接口,自己会收到该回调) func onRecvMessageReadReceipts(receiptList: Array<V2TIMMessageReceipt>) /// C2C 对端用户会话已读通知(如果对端用户调用 markC2CMessageAsRead 接口,自己会收到该通知) func onRecvC2CReadReceipt(receiptList: Array<V2TIMMessageReceipt>) /// 收到消息撤回 func onRecvMessageRevoked(msgID: String, operateUser: V2TIMUserInfo, reason: String?) /// 消息内容被修改 func onRecvMessageModified(msg: V2TIMMessage)}/// 高级消息监听器@protocol V2TIMAdvancedMsgListener <NSObject>@optional/// 收到新消息- (void)onRecvNewMessage:(V2TIMMessage *)msg;/// 消息已读回执通知(如果自己发的消息支持已读回执,消息接收端调用了 sendMessageReadReceipts 接口,自己会收到该回调)- (void)onRecvMessageReadReceipts:(NSArray<V2TIMMessageReceipt *> *)receiptList;/// C2C 对端用户会话已读通知(如果对端用户调用 markC2CMessageAsRead 接口,自己会收到该通知)- (void)onRecvC2CReadReceipt:(NSArray<V2TIMMessageReceipt *> *)receiptList;/// 收到消息撤回- (void)onRecvMessageRevoked:(NSString *)msgID;/// 消息内容被修改- (void)onRecvMessageModified:(V2TIMMessage *)msg;@endclass AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: AdvancedMsgListener() = default; ~AdvancedMsgListener() override = default; // 收到新消息 void OnRecvNewMessage(const V2TIMMessage& message) override {} // C2C 对端用户会话已读通知(对端用户调用 markC2CMessageAsRead,自己会收到该通知) void OnRecvC2CReadReceipt(const V2TIMMessageReceiptVector& receiptList) override {} // 消息已读回执通知(如果自己发送的消息支持已读回执,消息接收端调用 // sendMessageReadReceipts,自己会收到该通知) void OnRecvMessageReadReceipts(const V2TIMMessageReceiptVector& receiptList) override {} // 收到消息撤回的通知 void OnRecvMessageRevoked(const V2TIMString& messageID) override {} // 消息内容被修改 void OnRecvMessageModified(const V2TIMMessage& message) override {}};

十、移除监听器

如果想停止接收消息,接收方可调用 removeAdvancedMsgListener (Java / Swift / Objective-C / C++) 移除高级消息监听器。

示例代码如下:

JavaSwiftObjective-CC++V2TIMManager.getMessageManager().removeAdvancedMsgListener(advancedMsgListener);V2TIMManager.shared.removeAdvancedMsgListener(listener: self)// self 为 id<V2TIMAdvancedMsgListener>[[V2TIMManager sharedInstance] removeAdvancedMsgListener:self];class AdvancedMsgListener final : public V2TIMAdvancedMsgListener { // 成员 …};// advancedMsgListener 是 AdvancedMsgListener 的实例V2TIMManager::GetInstance()->GetMessageManager()->RemoveAdvancedMsgListener(&advancedMsgListener);

十三、单聊文本消息

接收方使用简单消息监听器接收单聊文本消息,需要以下几步:

1. 调用 addSimpleMsgListener 设置事件监听器。

2. 监听 onRecvC2CTextMessage (Java / Swift / Objective-C / C++) 回调,在其中接收文本消息。

3. 希望停止接收消息,调用 removeSimpleMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。

代码示例如下:

JavaSwiftObjective-CC++// 设置事件监听器V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);// 接收单聊文本消息/** * 收到 C2C 文本消息 * * @param msgID 消息唯一标识 * @param sender 发送方信息 * @param text 发送内容 */public void onRecvC2CTextMessage(String msgID, V2TIMUserInfo sender, String text) { // 可解析消息并展示到 UI}// 设置事件监听器V2TIMManager.shared.addSimpleMsgListener(listener: self)/// 接收单聊文本消息/// @param msgID 消息 Id/// @param sender 发送者信息/// @param text 文本内容func onRecvC2CTextMessage(msgID: String, sender: V2TIMUserInfo, text: String?) { // 可解析消息并展示到 UI}// 设置事件监听器[[V2TIMManager sharedInstance] addSimpleMsgListener:self];/// 接收单聊文本消息/// @param msgID 消息 Id/// @param info 发送者信息/// @param text 文本内容- (void)onRecvC2CTextMessage:(NSString *)msgID sender:(V2TIMUserInfo *)info text:(NSString *)text { // 可解析消息并展示到 UI}class SimpleMsgListener final : public V2TIMSimpleMsgListener {public: /** * 收到 C2C 文本消息 * * @param msgID 消息唯一标识 * @param sender 发送方信息 * @param text 发送内容 */ void OnRecvC2CTextMessage(const V2TIMString& msgID, const V2TIMUserFullInfo& sender, const V2TIMString& text) override { // 可以解析消息并展示到 UI,比如: std::cout << "text:" << std::string{text.CString(), text.Size()} << std::endl; } // 其他成员 …};// 添加基本消息的事件监听器,注意在移除监听器之前需要保持 simpleMsgListener 的生命期,以免接收不到事件回调SimpleMsgListener simpleMsgListener;V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

十四、群聊文本消息

接收方使用简单消息监听器接收群聊文本消息,需要以下几步:

1. 调用 addSimpleMsgListener 设置事件监听器。

2. 监听 onRecvGroupTextMessage (Java / Swift /Objective-C / C++) 回调,在其中接收文本消息。

3. 希望停止接收消息,调用 removeSimpleMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。

代码示例如下:

JavaSwiftObjective-CC++// 设置事件监听器V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);// 接收群聊文本消息/** * 收到群文本消息 * * @param msgID 消息唯一标识 * @param groupID 群 ID * @param sender 发送方群成员信息 * @param text 发送内容 */public void onRecvGroupTextMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, String text) { // 可解析消息并展示到 UI}// 设置事件监听器V2TIMManager.shared.addSimpleMsgListener(listener: self)/// 接收群聊文本消息/// @param msgID 消息 Id/// @param groupID 群组 ID/// @param sender 发送者信息/// @param text 文本内容func onRecvGroupTextMessage(msgID: String, groupID: String, sender: V2TIMGroupMemberInfo, text:String? { // 可解析消息并展示到 UI}// 设置事件监听器[[V2TIMManager sharedInstance] addSimpleMsgListener:self];/// 接收群聊文本消息/// @param msgID 消息 Id/// @param groupID 群组 ID/// @param info 发送者信息/// @param text 文本内容- (void)onRecvGroupTextMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info text:(NSString *)text { // 可解析消息并展示到 UI}class SimpleMsgListener final : public V2TIMSimpleMsgListener {public: /** * 收到群文本消息 * * @param msgID 消息唯一标识 * @param groupID 群 ID * @param sender 发送方群成员信息 * @param text 发送内容 */ void OnRecvGroupTextMessage(const V2TIMString& msgID, const V2TIMString& groupID, const V2TIMGroupMemberFullInfo& sender, const V2TIMString& text) override { // 可以解析消息并展示到 UI,比如: std::cout << "text:" << std::string{text.CString(), text.Size()} << std::endl; } // 其他成员 …};// 添加基本消息的事件监听器,注意在移除监听器之前需要保持 simpleMsgListener 的生命期,以免接收不到事件回调SimpleMsgListener simpleMsgListener;V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

十五、使用高级消息监听器接收

接收方使用高级消息监听器接收单聊、群聊文本消息,需要以下几步:

1. 调用 addAdvancedMsgListener 设置事件监听器。

2. 监听 onRecvNewMessage (Java / Swift / Objective-C / C++) 回调,在其中接收文本消息。

3. 希望停止接收消息,调用 removeAdvancedMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。

代码示例如下:

JavaSwiftObjective-CC++// 设置事件监听器V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener);/** * 收到新消息 * @param msg 消息 */public void onRecvNewMessage(V2TIMMessage msg) { // 解析出 groupID 和 userID String groupID = msg.getGroupID(); String userID = msg.getUserID(); // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 msg 中的文本消息 if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) { V2TIMTextElem textElem = msg.getTextElem(); String text = textElem.getText(); Log.i("onRecvNewMessage", "text:" + text); }}// 设置事件监听器V2TIMManager.shared.addAdvancedMsgListener(listener: self)func onRecvNewMessage(_ msg: V2TIMMessage) { // 解析出 groupID 和 userID let groupID = msg.groupID let userID = msg.userID // 判断当前是单聊还是群聊 // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 if !groupID.isEmpty { print("Received a group message in group: \\(groupID)") } else if !userID.isEmpty { print("Received a one-on-one message from user: \\(userID)") } else { print("Received a message with no identifiable sender.") } // 解析出 msg 中的文本消息 if msg.elemType == .V2TIM_ELEM_TYPE_TEXT { if let textElem = msg.textElem { let text = textElem.text print("onRecvNewMessage, text: \\(text)") } } else { print("Received a non-text message.") }}// 设置事件监听器[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];/// 接收消息/// @param msg 消息对象- (void)onRecvNewMessage:(V2TIMMessage *)msg { // 解析出 groupID 和 userID NSString *groupID = msg.groupID; NSString *userID = msg.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 msg 中的文本消息 if (msg.elemType == V2TIM_ELEM_TYPE_TEXT) { V2TIMTextElem *textElem = msg.textElem; NSString *text = textElem.text; NSLog(@"onRecvNewMessage, text: %@", text); }}class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的文本消息 if (message.elemList.Size() == 1) { V2TIMElem* elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_TEXT) { // 文本消息 auto textElem = static_cast<V2TIMTextElem*>(elem); // 消息文本 V2TIMString text = textElem->text; // 可以解析消息并展示到 UI,比如: std::cout << "text:" << std::string{text.CString(), text.Size()} << std::endl; } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

十八、单聊自定义消息

接收方使用简单消息监听器接收单聊自定义消息,需要以下几步:

1. 调用 addSimpleMsgListener 设置事件监听器。

2. 监听 onRecvC2CCustomMessage (Java / Swift / Objective-C / C++) 回调,在其中接收单聊自定义消息。

3. 希望停止接收消息,调用 removeSimpleMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。

代码示例如下:

JavaSwiftObjective-CC++/** * 接收单聊自定义消息 * @param msgID 消息 ID * @param sender 发送方信息 * @param customData 发送内容 */public void onRecvC2CCustomMessage(String msgID, V2TIMUserInfo sender, byte[] customData) { Log.i("onRecvC2CCustomMessage", "msgID:" + msgID + ", from:" + sender.getNickName() + ", content:" + new String(customData));}/// 接收单聊自定义消息/// @param msgID 消息 ID/// @param sender 发送者信息/// @param customData 自定义消息二进制内容func onRecvC2CCustomMessage(msgID: String, sender: V2TIMUserInfo, customData: Data?) {}/// 接收单聊自定义消息/// @param msgID 消息 ID/// @param info 发送者信息/// @param data 自定义消息二进制内容- (void)onRecvC2CCustomMessage:(NSString *)msgID sender:(V2TIMUserInfo *)info customData:(NSData *)data { NSLog(@"onRecvC2CCustomMessage, msgID: %@, sender: %@, customData: %@", msgID, info, data);}class SimpleMsgListener final : public V2TIMSimpleMsgListener {public: /** * 收到 C2C 自定义(信令)消息 * * @param msgID 消息唯一标识 * @param sender 发送方信息 * @param customData 发送内容 */ void OnRecvC2CCustomMessage(const V2TIMString& msgID, const V2TIMUserFullInfo& sender, const V2TIMBuffer& customData) override { // 可以解析消息并展示到 UI,比如当 customData 为文本时: std::cout << "customData:" << std::string{reinterpret_cast<const char*>(customData.Data()), customData.Size()} << std::endl; } // 其他成员 …};// 添加基本消息的事件监听器,注意在移除监听器之前需要保持 simpleMsgListener 的生命期,以免接收不到事件回调SimpleMsgListener simpleMsgListener;V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

十九、群聊自定义消息

接收方使用简单消息监听器接收群聊自定义消息,需要以下几步:

1. 调用 addSimpleMsgListener 设置事件监听器。

2. 监听 onRecvGroupCustomMessage (Java / Swift / Objective-C / C++) 回调,在其中接收群聊自定义消息。

3. 希望停止接收消息,调用 removeSimpleMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。

JavaSwiftObjective-CC++/** * 接收群聊自定义消息 * @param msgID 消息 ID * @param groupID 群 ID * @param sender 发送方群成员信息 * @param customData 发送内容 */public void onRecvGroupCustomMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, byte[] customData) { Log.i("onRecvGroupCustomMessage", "msgID:" + msgID + ", groupID:" + groupID + ", from:" + sender.getNickName() + ", content:" + new String(customData));}/// 接收群聊自定义消息/// @param msgID 消息 ID/// @param groupID 群组 ID/// @param sender 发送者信息/// @param customData 自定义消息二进制内容func onRecvGroupCustomMessage(msgID: String, groupID: String, sender: V2TIMGroupMemberInfo, customData: Data?) {}/// 接收群聊自定义消息/// @param msgID 消息 ID/// @param groupID 群组 ID/// @param info 发送者信息/// @param text 自定义消息二进制内容- (void)onRecvGroupCustomMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info customData:(NSData *)data { NSLog(@"onRecvGroupCustomMessage, msgID: %@, groupID: %@, sender: %@, customData: %@", msgID, groupID, info, data);}class SimpleMsgListener final : public V2TIMSimpleMsgListener {public: /** * 收到群自定义(信令)消息 * * @param msgID 消息唯一标识 * @param groupID 群 ID * @param sender 发送方群成员信息 * @param customData 发送内容 */ void OnRecvGroupCustomMessage(const V2TIMString& msgID, const V2TIMString& groupID, const V2TIMGroupMemberFullInfo& sender, const V2TIMBuffer& customData) override { // 可以解析消息并展示到 UI,比如当 customData 为文本时: std::cout << "customData:" << std::string{reinterpret_cast<const char*>(customData.Data()), customData.Size()} << std::endl; } // 其他成员 …};// 添加基本消息的事件监听器,注意在移除监听器之前需要保持 simpleMsgListener 的生命期,以免接收不到事件回调SimpleMsgListener simpleMsgListener;V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

20、使用高级消息监听器接收

接收方使用高级消息监听器接收单聊、群聊自定义消息,需要以下几步:

1. 调用 addAdvancedMsgListener 设置事件监听器。

2. 监听 onRecvNewMessage (Java / Swift / Objective-C / C++) 回调,在其中接收自定义消息。

3. 希望停止接收消息,调用 removeAdvancedMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。

代码示例如下:

JavaSwiftObjective-CC++// 设置事件监听器V2TIMManager.getMessageManager().addAdvancedMsgListener(v2TIMAdvancedMsgListener);// 接收消息public void onRecvNewMessage(V2TIMMessage msg) { // 解析出 groupID 和 userID String groupID = msg.getGroupID(); String userID = msg.getUserID(); // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 msg 中的自定义消息 if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_CUSTOM) { V2TIMCustomElem customElem = msg.getCustomElem(); String data = new String(customElem.getData()); Log.i("onRecvNewMessage", "customData:" + data); }}// 设置事件监听器V2TIMManager.shared.addAdvancedMsgListener(listener: self)/// 接收消息/// @param msg 消息对象func onRecvNewMessage(_ msg: V2TIMMessage) { // 解析出 groupID 和 userID let groupID = msg.groupID let userID = msg.userID // 判断当前是单聊还是群聊 // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 if !groupID.isEmpty { print("Received a group message in group: \\(groupID)") } else if !userID.isEmpty { print("Received a one-on-one message from user: \\(userID)") } else { print("Received a message with no identifiable sender.") } // 解析出 msg 中的文本消息 if msg.elemType == .V2TIM_ELEM_TYPE_TEXT { if let textElem = msg.textElem { let text = textElem.text print("onRecvNewMessage, text: \\(text)") } } else { print("Received a non-text message.") }}// 设置事件监听器[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];/// 接收消息/// @param msg 消息对象- (void)onRecvNewMessage:(V2TIMMessage *)msg { // 解析出 groupID 和 userID NSString *groupID = msg.groupID; NSString *userID = msg.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 msg 中的自定义消息 if (msg.elemType == V2TIM_ELEM_TYPE_CUSTOM) { V2TIMCustomElem *customElem = msg.customElem; NSData *customData = customElem.data; NSLog(@"onRecvNewMessage, customData: %@", customData); }}class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的自定义消息 if (message.elemList.Size() == 1) { V2TIMElem* elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_CUSTOM) { // 自定义消息 auto customElem = static_cast<V2TIMCustomElem*>(elem); // 自定义消息二进制数据 V2TIMBuffer data = customElem->data; // 可以解析消息并展示到 UI,比如当 data 为文本时: std::cout << "data:" << std::string{reinterpret_cast<const char*>(data.Data()), data.Size()} << std::endl; } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

21、接收富媒体消息

接收富媒体消息只能使用高级消息监听器,需要以下几步:

1. 接收方调用 addAdvancedMsgListener 接口设置高级消息监听。

2. 接收方通过监听回调 onRecvNewMessage (Java/ Swift / Objective-C / C++) 获取消息 V2TIMMessage。

3. 接收方解析 V2TIMMessage 消息中的 elemType 属性,并根据其类型进行二次解析,获取消息内部 Elem 中的具体内容。

4. 希望停止接收消息,调用 removeAdvancedMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。

22、图片消息

一个图片消息会包含三种格式大小的图片,分别为原图、大图、微缩图(SDK 会在发送图片消息的时候自动生成微缩图、大图,客户不需要关心):

·大图:将原图等比压缩。压缩后宽、高中较小的一个等于 720 像素。

·缩略图:将原图等比压缩。压缩后宽、高中较小的一个等于 198 像素。

接收端收到图片消息后,我们推荐你调用 SDK 的 downloadImage (Java / Swift / Objective-C / C++) 将图片下载到本地,再取出图片渲染到 UI 层。

为了避免重复下载,节省资源,我们推荐你将 V2TIMImage 对象的 uuid 属性值设置到图片的下载路径中,作为图片的标识。

示例代码向你演示如何从 V2TIMMessage 中解析出图片消息内容:

JavaSwiftObjective-CC++public void onRecvNewMessage(V2TIMMessage msg) { if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE) { // 图片消息 V2TIMImageElem v2TIMImageElem = msg.getImageElem(); // 一个图片消息会包含三种格式大小的图片,分别为原图、大图、微缩图(SDK 会在发送图片消息的时候自动生成微缩图、大图,客户不需要关心) // 大图:是将原图等比压缩,压缩后宽、高中较小的一个等于720像素。 // 缩略图:是将原图等比压缩,压缩后宽、高中较小的一个等于198像素。 List<V2TIMImageElem.V2TIMImage> imageList = v2TIMImageElem.getImageList(); for (V2TIMImageElem.V2TIMImage v2TIMImage : imageList) { // 图片 ID,内部标识,可用于外部缓存 key String uuid = v2TIMImage.getUUID(); // 图片类型,三种类型,分别为 V2TIM_IMAGE_TYPE_ORIGIN(原图),V2TIM_IMAGE_TYPE_THUMB(缩略图)和 V2TIM_IMAGE_TYPE_LARGE(大图) int imageType = v2TIMImage.getType(); // 图片大小(字节) int size = v2TIMImage.getSize(); // 图片宽度 int width = v2TIMImage.getWidth(); // 图片高度 int height = v2TIMImage.getHeight(); // 图片的原始下载地址 String url = v2TIMImage.getUrl(); // 设置图片下载路径 imagePath,这里可以用 uuid 作为标识,避免重复下载 String imagePath = "/sdcard/im/image/" + "myUserID" + uuid; File imageFile = new File(imagePath); // 判断 imagePath 下有没有已经下载过的图片文件 if (!imageFile.exists()) { // 下载图片 v2TIMImage.downloadImage(imagePath, new V2TIMDownloadCallback() { @Override public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) { // 下载进度回调:已下载大小 v2ProgressInfo.getCurrentSize();总文件大小 v2ProgressInfo.getTotalSize() } @Override public void onError(int code, String desc) { // 下载失败 } @Override public void onSuccess() { // 下载完成 } }); } else { // 图片已存在 } } }}func onRecvNewMessage(_ msg: V2TIMMessage) { if msg.elemType == .V2TIM_ELEM_TYPE_IMAGE { guard let imageElem = msg.imageElem else { return } // 原图、大图、微缩图列表 let imageList = imageElem.imageList for timImage in imageList { // 图片 ID,内部标识,可用于外部缓存 key let uuid = timImage.uuid // 图片类型 let type = timImage.type // 图片大小(字节) let size = timImage.size // 图片宽度 let width = timImage.width // 图片高度 let height = timImage.height // 图片的原始下载地址 let url = timImage.url // 设置图片下载路径 imagePath,这里可以用 uuid 作为标识,避免重复下载 let imagePath = NSTemporaryDirectory().appending("testImage\\(timImage.uuid)") // 判断 imagePath 下有没有已经下载过的图片文件 if !FileManager.default.fileExists(atPath: imagePath) { // 下载图片 timImage.downloadImage(path: imagePath, progress: { curSize, totalSize in // 下载进度 print("下载图片进度:curSize:\\(curSize), totalSize: \\(totalSize)") }, succ: { // 下载成功 print("下载图片完成") }, fail: { code, msg in // 下载失败 print("下载图片失败:code:\\(code), msg: \\(msg)") }) } else { // 图片已存在 print("图片已存在:\\(imagePath)") } print("图片信息:uuid: \\(uuid), type: \\(type.rawValue), size: \\(size), width: \\(width), height: \\(height)") } }}- (void)onRecvNewMessage:(V2TIMMessage *)msg { if (msg.elemType == V2TIM_ELEM_TYPE_IMAGE) { V2TIMImageElem *imageElem = msg.imageElem; // 原图、大图、微缩图列表 NSArray<V2TIMImage *> *imageList = imageElem.imageList; for (V2TIMImage *timImage in imageList) { // 图片 ID,内部标识,可用于外部缓存 key NSString *uuid = timImage.uuid; // 图片类型 V2TIMImageType type = timImage.type; // 图片大小(字节) int size = timImage.size; // 图片宽度 int width = timImage.width; // 图片高度 int height = timImage.height; // 图片的原始下载地址 NSString * url = timImage.url; // 设置图片下载路径 imagePath,这里可以用 uuid 作为标识,避免重复下载 NSString *imagePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testImage%@", timImage.uuid]]; // 判断 imagePath 下有没有已经下载过的图片文件 if (![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) { // 下载图片 [timImage downloadImage:imagePath progress:^(NSInteger curSize, NSInteger totalSize) { // 下载进度 NSLog(@"下载图片进度:curSize:%lu,totalSize:%lu",curSize,totalSize); } succ:^{ // 下载成功 NSLog(@"下载图片完成"); } fail:^(int code, NSString *msg) { // 下载失败 NSLog(@"下载图片失败:code:%d,msg:%@",code,msg); }]; } else { // 图片已存在 } NSLog(@"图片信息:uuid:%@, type:%ld, size:%d, width:%d, height:%d", uuid, (long)type, size, width, height); } }}class DownloadCallback final : public V2TIMDownloadCallback {public: using SuccessCallback = std::function<void()>; using ErrorCallback = std::function<void(int, const V2TIMString&)>; using DownLoadProgressCallback = std::function<void(uint64_t, uint64_t)>; DownloadCallback() = default; ~DownloadCallback() override = default; void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback, DownLoadProgressCallback download_progress_callback) { success_callback_ = std::move(success_callback); error_callback_ = std::move(error_callback); download_progress_callback_ = std::move(download_progress_callback); } void OnSuccess() override { if (success_callback_) { success_callback_(); } } void OnError(int error_code, const V2TIMString& error_message) override { if (error_callback_) { error_callback_(error_code, error_message); } } void OnDownLoadProgress(uint64_t currentSize, uint64_t totalSize) override { if (download_progress_callback_) { download_progress_callback_(currentSize, totalSize); } }private: SuccessCallback success_callback_; ErrorCallback error_callback_; DownLoadProgressCallback download_progress_callback_;};class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的图片消息 if (message.elemList.Size() == 1) { V2TIMElem* elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_IMAGE) { // 图片消息 auto imageElem = static_cast<V2TIMImageElem*>(elem); // 一个图片消息会包含三种格式大小的图片,分别为原图、大图、微缩图(SDK // 会在发送图片消息的时候自动生成微缩图、大图,客户不需要关心) // 大图:是将原图等比压缩,压缩后宽、高中较小的一个等于720像素。 // 缩略图:是将原图等比压缩,压缩后宽、高中较小的一个等于198像素。 V2TIMImageVector imageList = imageElem->imageList; for (size_t i = 0; i < imageList.Size(); ++i) { V2TIMImage& image = imageList[i]; /// 图片 ID,内部标识,可用于外部缓存 key V2TIMString uuid = image.uuid; /// 图片类型 V2TIMImageType type = image.type; /// 图片大小(type == V2TIMImageType::V2TIM_IMAGE_TYPE_ORIGIN 有效) uint64_t size = image.size; /// 图片宽度 uint32_t width = image.width; /// 图片高度 uint32_t height = image.height; // 图片的原始下载地址 V2TIMString url = image.url; // 设置图片下载路径 path,这里可以用 uuid 作为标识,避免重复下载 std::filesystem::path imagePath = u8"./File/Image/"s + uuid.CString(); // 判断 imagePath 下有没有已经下载过的图片文件 if (!std::filesystem::exists(imagePath)) { std::filesystem::create_directories(imagePath.parent_path()); auto callback = new DownloadCallback{}; callback->SetCallback( [=]() { // 下载完成 delete callback; }, [=](int error_code, const V2TIMString& error_message) { // 下载失败 delete callback; }, [=](uint64_t currentSize, uint64_t totalSize) { // 下载进度回调:已下载大小 currentSize; 总文件大小 totalSize }); image.DownloadImage(imagePath.string().c_str(), callback); } else { // 图片已存在 } } } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

23、视频消息

接收方收到视频消息后,一般需要在聊天界面显示一个视频预览图,当用户点击消息后,才会触发视频的播放。
所以这里需要两步:

1. 下载视频截图。我们推荐你调用 SDK 的 downloadSnapshot (Java / Swift / Objective-C / C++) 进行下载。

2. 下载视频。我们推荐你调用 SDK 的 downloadVideo (Java / Swift / Objective-C / C++) 进行下载。

为了避免重复下载,节省资源,我们推荐你将 V2TIMVideoElem 对象的 videoUUID 属性值设置到视频的下载路径中,作为视频的标识。

示例代码向你演示如何从 V2TIMMessage 中解析出视频消息内容:

JavaSwiftObjective-CC++public void onRecvNewMessage(V2TIMMessage msg) { if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_VIDEO) { // 视频消息 V2TIMVideoElem v2TIMVideoElem = msg.getVideoElem(); // 视频截图 ID,内部标识,可用于外部缓存 key String snapshotUUID = v2TIMVideoElem.getSnapshotUUID(); // 视频截图文件大小 int snapshotSize = v2TIMVideoElem.getSnapshotSize(); // 视频截图宽 int snapshotWidth = v2TIMVideoElem.getSnapshotWidth(); // 视频截图高 int snapshotHeight = v2TIMVideoElem.getSnapshotHeight(); // 视频 ID,内部标识,可用于外部缓存 key String videoUUID = v2TIMVideoElem.getVideoUUID(); // 视频文件大小 int videoSize = v2TIMVideoElem.getVideoSize(); // 视频时长 int duration = v2TIMVideoElem.getDuration(); // 设置视频截图文件路径,这里可以用 uuid 作为标识,避免重复下载 String snapshotPath = "/sdcard/im/snapshot/" + "myUserID" + snapshotUUID; File snapshotFile = new File(snapshotPath); if (!snapshotFile.exists()) { v2TIMVideoElem.downloadSnapshot(snapshotPath, new V2TIMDownloadCallback() { @Override public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) { // 下载进度回调:已下载大小 v2ProgressInfo.getCurrentSize();总文件大小 v2ProgressInfo.getTotalSize() } @Override public void onError(int code, String desc) { // 下载失败 } @Override public void onSuccess() { // 下载完成 } }); } else { // 文件已存在 } // 设置视频文件路径,这里可以用 uuid 作为标识,避免重复下载 String videoPath = "/sdcard/im/video/" + "myUserID" + videoUUID; File videoFile = new File(videoPath); if (!videoFile.exists()) { v2TIMVideoElem.downloadVideo(videoPath, new V2TIMDownloadCallback() { @Override public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) { // 下载进度回调:已下载大小 v2ProgressInfo.getCurrentSize();总文件大小 v2ProgressInfo.getTotalSize() } @Override public void onError(int code, String desc) { // 下载失败 } @Override public void onSuccess() { // 下载完成 } }); } else { // 文件已存在 } }}func onRecvNewMessage(_ msg: V2TIMMessage) { if msg.elemType == .V2TIM_ELEM_TYPE_VIDEO { let videoElem = msg.videoElem // 视频截图 ID, 内部标识,可用于外部缓存 key let snapshotUUID = videoElem?.snapshotUUID // 视频截图文件大小 let snapshotSize = videoElem?.snapshotSize // 视频截图宽 let snapshotWidth = videoElem?.snapshotWidth // 视频截图高 let snapshotHeight = videoElem?.snapshotHeight // 视频 ID, 内部标识,可用于外部缓存 key let videoUUID = videoElem?.videoUUID // 视频文件大小 let videoSize = videoElem?.videoSize // 视频时长 let duration = videoElem?.duration // 设置视频截图文件路径,这里可以用 uuid 作为标识,避免重复下载 let snapshotPath = NSTemporaryDirectory().appending("testVideoSnapshot\\(snapshotUUID)") if !FileManager.default.fileExists(atPath: snapshotPath) { // 下载视频截图 videoElem?.downloadSnapshot(path: snapshotPath, progress: { curSize, totalSize in // 下载进度 print("下载视频截图进度:curSize:\\(curSize), totalSize: \\(totalSize)") }, succ: { // 下载成功 print("下载视频截图完成") }, fail: { code, msg in // 下载失败 print("下载视频截图失败:code:\\(code), msg: \\(msg)") }) } else { // 视频截图已存在 } print("视频截图信息:snapshotUUID: \\(snapshotUUID), snapshotSize: \\(snapshotSize), snapshotWidth: \\(snapshotWidth), snapshotHeight: \\(snapshotHeight), snapshotPath: \\(snapshotPath)") // 设置视频文件路径,这里可以用 uuid 作为标识,避免重复下载 let videoPath = NSTemporaryDirectory().appending("testVideo\\(videoUUID)") if !FileManager.default.fileExists(atPath: videoPath) { // 下载视频 videoElem?.downloadVideo(path: videoPath, progress: { curSize, totalSize in // 下载进度 print("下载视频进度:curSize:\\(curSize), totalSize: \\(totalSize)") }, succ: { // 下载成功 print("下载视频完成") }, fail: { code, msg in // 下载失败 print("下载视频失败:code:\\(code), msg: \\(msg)") }) } else { // 视频已存在 } print("视频信息:videoUUID: \\(videoUUID), videoSize: \\(videoSize), duration: \\(duration), videoPath: \\(videoPath)") }}- (void)onRecvNewMessage:(V2TIMMessage *)msg { if (msg.elemType == V2TIM_ELEM_TYPE_VIDEO) { V2TIMVideoElem *videoElem = msg.videoElem; // 视频截图 ID,内部标识,可用于外部缓存 key NSString *snapshotUUID = videoElem.snapshotUUID; // 视频截图文件大小 int snapshotSize = videoElem.snapshotSize; // 视频截图宽 int snapshotWidth = videoElem.snapshotWidth; // 视频截图高 int snapshotHeight = videoElem.snapshotHeight; // 视频 ID,内部标识,可用于外部缓存 key NSString *videoUUID = videoElem.videoUUID; // 视频文件大小 int videoSize = videoElem.videoSize; // 视频时长 int duration = videoElem.duration; // 设置视频截图文件路径,这里可以用 uuid 作为标识,避免重复下载 NSString *snapshotPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testVideoSnapshot%@",snapshotUUID]]; if (![[NSFileManager defaultManager] fileExistsAtPath:snapshotPath]) { // 下载视频截图 [videoElem downloadSnapshot:snapshotPath progress:^(NSInteger curSize, NSInteger totalSize) { // 下载进度 NSLog(@"%@", [NSString stringWithFormat:@"下载视频截图进度:curSize:%lu,totalSize:%lu",curSize,totalSize]); } succ:^{ // 下载成功 NSLog(@"下载视频截图完成"); } fail:^(int code, NSString *msg) { // 下载失败 NSLog(@"%@", [NSString stringWithFormat:@"下载视频截图失败:code:%d,msg:%@",code,msg]); }]; } else { // 视频截图已存在 } NSLog(@"视频截图信息:snapshotUUID:%@, snapshotSize:%d, snapshotWidth:%d, snapshotWidth:%d, snapshotPath:%@", snapshotUUID, snapshotSize, snapshotWidth, snapshotHeight, snapshotPath); // 设置视频文件路径,这里可以用 uuid 作为标识,避免重复下载 NSString *videoPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testVideo%@",videoUUID]]; if (![[NSFileManager defaultManager] fileExistsAtPath:videoPath]) { // 下载视频 [videoElem downloadVideo:videoPath progress:^(NSInteger curSize, NSInteger totalSize) { // 下载进度 NSLog(@"%@", [NSString stringWithFormat:@"下载视频进度:curSize:%lu,totalSize:%lu",curSize,totalSize]); } succ:^{ // 下载成功 NSLog(@"下载视频完成"); } fail:^(int code, NSString *msg) { // 下载失败 NSLog(@"%@", [NSString stringWithFormat:@"下载视频失败:code:%d,msg:%@",code,msg]); }]; } else { // 视频已存在 } NSLog(@"视频信息:videoUUID:%@, videoSize:%d, duration:%d, videoPath:%@", videoUUID, videoSize, duration, videoPath); }}class DownloadCallback final : public V2TIMDownloadCallback {public: using SuccessCallback = std::function<void()>; using ErrorCallback = std::function<void(int, const V2TIMString&)>; using DownLoadProgressCallback = std::function<void(uint64_t, uint64_t)>; DownloadCallback() = default; ~DownloadCallback() override = default; void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback, DownLoadProgressCallback download_progress_callback) { success_callback_ = std::move(success_callback); error_callback_ = std::move(error_callback); download_progress_callback_ = std::move(download_progress_callback); } void OnSuccess() override { if (success_callback_) { success_callback_(); } } void OnError(int error_code, const V2TIMString& error_message) override { if (error_callback_) { error_callback_(error_code, error_message); } } void OnDownLoadProgress(uint64_t currentSize, uint64_t totalSize) override { if (download_progress_callback_) { download_progress_callback_(currentSize, totalSize); } }private: SuccessCallback success_callback_; ErrorCallback error_callback_; DownLoadProgressCallback download_progress_callback_;};class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的视频消息 if (message.elemList.Size() == 1) { V2TIMElem* elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_VIDEO) { // 视频消息 auto videoElem = static_cast<V2TIMVideoElem*>(elem); // 视频 ID,内部标识,可用于外部缓存 key V2TIMString videoUUID = videoElem->videoUUID; // 视频大小 uint64_t videoSize = videoElem->videoSize; // 视频类型 V2TIMString videoType = videoElem->videoType; // 视频时长 uint32_t duration = videoElem->duration; // 截图 ID,内部标识,可用于外部缓存 key V2TIMString snapshotUUID = videoElem->snapshotUUID; // 截图 size uint64_t snapshotSize = videoElem->snapshotSize; // 截图宽 uint32_t snapshotWidth = videoElem->snapshotWidth; // 截图高 uint32_t snapshotHeight = videoElem->snapshotHeight; // 设置视频文件路径,这里可以用 uuid 作为标识,避免重复下载 std::filesystem::path videoPath = u8"./File/Video/"s + videoUUID.CString(); // 判断 videoPath 下有没有已经下载过的视频文件 if (!std::filesystem::exists(videoPath)) { std::filesystem::create_directories(videoPath.parent_path()); auto callback = new DownloadCallback{}; callback->SetCallback( [=]() { // 下载完成 delete callback; }, [=](int error_code, const V2TIMString& error_message) { // 下载失败 delete callback; }, [=](uint64_t currentSize, uint64_t totalSize) { // 下载进度回调:已下载大小 currentSize; 总文件大小 totalSize }); videoElem->DownloadVideo(videoPath.string().c_str(), callback); } else { // 视频文件已存在 } // 设置视频截图文件路径,这里可以用 uuid 作为标识,避免重复下载 std::filesystem::path snapshotPath = u8"./File/Snapshot/"s + snapshotUUID.CString(); // 判断 snapshotPath 下有没有已经下载过的视频截图文件 if (!std::filesystem::exists(snapshotPath)) { std::filesystem::create_directories(snapshotPath.parent_path()); auto callback = new DownloadCallback{}; callback->SetCallback( [=]() { // 下载完成 delete callback; }, [=](int error_code, const V2TIMString& error_message) { // 下载失败 delete callback; }, [=](uint64_t currentSize, uint64_t totalSize) { // 下载进度回调:已下载大小 currentSize; 总文件大小 totalSize }); videoElem->DownloadSnapshot(snapshotPath.string().c_str(), callback); } else { //视频截图文件已存在 } } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

24、语音消息

接收端收到语音消息后,我们推荐你调用 SDK 的 downloadSound (Java / Swift / Objective-C / C++) 将语音下载到本地,再获取本地语音文件播放。

为了避免重复下载,节省资源,我们推荐你将 V2TIMSoundElem 对象的 uuid 属性值设置到语音的下载路径中,作为语音的标识。

示例代码向你演示如何从 V2TIMMessage 中解析出语音消息内容:

JavaSwiftObjective-CC++public void onRecvNewMessage(V2TIMMessage msg) { if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_SOUND) { // 语音消息 V2TIMSoundElem v2TIMSoundElem = msg.getSoundElem(); // 语音 ID,内部标识,可用于外部缓存 key String uuid = v2TIMSoundElem.getUUID(); // 语音文件大小 int dataSize = v2TIMSoundElem.getDataSize(); // 语音时长 int duration = v2TIMSoundElem.getDuration(); // 设置语音文件路径 soundPath,这里可以用 uuid 作为标识,避免重复下载 String soundPath = "/sdcard/im/sound/" + "myUserID" + uuid; File imageFile = new File(soundPath); // 判断 soundPath 下有没有已经下载过的语音文件 if (!imageFile.exists()) { v2TIMSoundElem.downloadSound(soundPath, new V2TIMDownloadCallback() { @Override public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) { // 下载进度回调:已下载大小 v2ProgressInfo.getCurrentSize();总文件大小 v2ProgressInfo.getTotalSize() } @Override public void onError(int code, String desc) { // 下载失败 } @Override public void onSuccess() { // 下载完成 } }); } else { // 文件已存在 } }}func onRecvNewMessage(_ msg: V2TIMMessage) { if msg.elemType == .V2TIM_ELEM_TYPE_SOUND { guard let soundElem = msg.soundElem else { return } // 语音 ID,内部标识,可用于外部缓存 key let uuid = soundElem.uuid // 语音文件大小 let dataSize = soundElem.dataSize // 语音时长 let duration = soundElem.duration // 设置语音文件路径 soundPath,这里可以用 uuid 作为标识,避免重复下载 let soundPath = NSTemporaryDirectory().appending("testSound\\(uuid)") // 判断 soundPath 下有没有已经下载过的语音文件 if !FileManager.default.fileExists(atPath: soundPath) { // 下载语音 soundElem.downloadSound(path: soundPath, progress: { curSize, totalSize in // 下载进度 print("下载语音进度:curSize:\\(curSize), totalSize: \\(totalSize)") }, succ: { // 下载成功 print("下载语音完成") }, fail: { code, msg in // 下载失败 print("下载语音失败:code:\\(code), msg: \\(msg)") }) } else { // 语音已存在 print("语音已存在:\\(soundPath)") } print("语音信息:uuid: \\(uuid), dataSize: \\(dataSize), duration: \\(duration), soundPath: \\(soundPath)") }}- (void)onRecvNewMessage:(V2TIMMessage *)msg { if (msg.elemType == V2TIM_ELEM_TYPE_SOUND) { V2TIMSoundElem *soundElem = msg.soundElem; // 语音 ID,内部标识,可用于外部缓存 key NSString *uuid = soundElem.uuid; // 语音文件大小 int dataSize = soundElem.dataSize; // 语音时长 int duration = soundElem.duration; // 设置语音文件路径 soundPath,这里可以用 uuid 作为标识,避免重复下载 NSString *soundPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testSound%@",uuid]]; // 判断 soundPath 下有没有已经下载过的语音文件 if (![[NSFileManager defaultManager] fileExistsAtPath:soundPath]) { // 下载语音 [soundElem downloadSound:soundPath progress:^(NSInteger curSize, NSInteger totalSize) { // 下载进度 NSLog(@"下载语音进度:curSize:%lu,totalSize:%lu",curSize,totalSize); } succ:^{ // 下载成功 NSLog(@"下载语音完成"); } fail:^(int code, NSString *msg) { // 下载失败 NSLog(@"下载语音失败:code:%d,msg:%@",code,msg); }]; } else { // 语音已存在 } NSLog(@"语音信息:uuid:%@, dataSize:%d, duration:%d, soundPath:%@", uuid, dataSize, duration, soundPath); }}class DownloadCallback final : public V2TIMDownloadCallback {public: using SuccessCallback = std::function<void()>; using ErrorCallback = std::function<void(int, const V2TIMString&)>; using DownLoadProgressCallback = std::function<void(uint64_t, uint64_t)>; DownloadCallback() = default; ~DownloadCallback() override = default; void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback, DownLoadProgressCallback download_progress_callback) { success_callback_ = std::move(success_callback); error_callback_ = std::move(error_callback); download_progress_callback_ = std::move(download_progress_callback); } void OnSuccess() override { if (success_callback_) { success_callback_(); } } void OnError(int error_code, const V2TIMString& error_message) override { if (error_callback_) { error_callback_(error_code, error_message); } } void OnDownLoadProgress(uint64_t currentSize, uint64_t totalSize) override { if (download_progress_callback_) { download_progress_callback_(currentSize, totalSize); } }private: SuccessCallback success_callback_; ErrorCallback error_callback_; DownLoadProgressCallback download_progress_callback_;};class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的语音消息 if (message.elemList.Size() == 1) { V2TIMElem* elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_SOUND) { // 语音消息 auto soundElem = static_cast<V2TIMSoundElem*>(elem); // 语音消息内部 ID V2TIMString uuid = soundElem->uuid; // 语音数据大小 uint64_t dataSize = soundElem->dataSize; // 语音长度(秒) uint32_t duration = soundElem->duration; // 设置语音文件路径,这里可以用 uuid 作为标识,避免重复下载 std::filesystem::path soundPath = u8"./File/Sound/"s + uuid.CString(); // 判断 soundPath 下有没有已经下载过的语音文件 if (!std::filesystem::exists(soundPath)) { std::filesystem::create_directories(soundPath.parent_path()); auto callback = new DownloadCallback{}; callback->SetCallback( [=]() { // 下载完成 delete callback; }, [=](int error_code, const V2TIMString& error_message) { // 下载失败 delete callback; }, [=](uint64_t currentSize, uint64_t totalSize) { // 下载进度回调:已下载大小 currentSize; 总文件大小 totalSize }); soundElem->DownloadSound(soundPath.string().c_str(), callback); } else { // 语音文件已存在 } } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

25、文件消息

接收端收到文件消息后,我们推荐你调用 SDK 的 downloadFile (Java / Swift / Objective-C / C++) 将文件下载到本地,再获取本地文件展示。

为了避免重复下载,节省资源,我们推荐你将 V2TIMFileElem 对象的 uuid 属性值设置到文件的下载路径中,作为文件的标识。

示例代码向你演示如何从 V2TIMMessage 中解析出文件消息内容:

JavaSwiftObjective-CC++public void onRecvNewMessage(V2TIMMessage msg) { if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_FILE) { // 文件消息 V2TIMFileElem v2TIMFileElem = msg.getFileElem(); // 文件 ID,内部标识,可用于外部缓存 key String uuid = v2TIMFileElem.getUUID(); // 文件名称 String fileName = v2TIMFileElem.getFileName(); // 文件大小 int fileSize = v2TIMFileElem.getFileSize(); // 设置文件路径,这里可以用 uuid 作为标识,避免重复下载 String filePath = "/sdcard/im/file/" + "myUserID" + uuid; File file = new File(filePath); if (!file.exists()) { v2TIMFileElem.downloadFile(filePath, new V2TIMDownloadCallback() { @Override public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) { // 下载进度回调:已下载大小 v2ProgressInfo.getCurrentSize();总文件大小 v2ProgressInfo.getTotalSize() } @Override public void onError(int code, String desc) { // 下载失败 } @Override public void onSuccess() { // 下载完成 } }); } else { // 文件已存在 } }}func onRecvNewMessage(_ msg: V2TIMMessage) { if msg.elemType == .V2TIM_ELEM_TYPE_FILE { guard let fileElem = msg.fileElem else { return } // 文件 ID,内部标识,可用于外部缓存 key let uuid = fileElem.uuid // 文件名称 let filename = fileElem.filename // 文件大小 let fileSize = fileElem.fileSize // 设置文件路径,这里可以用 uuid 作为标识,避免重复下载 let filePath = NSTemporaryDirectory().appending("testFile\\(uuid)") if !FileManager.default.fileExists(atPath: filePath) { // 下载文件 fileElem.downloadFile(path: filePath, progress: { curSize, totalSize in // 下载进度 print("下载文件进度:curSize:\\(curSize), totalSize: \\(totalSize)") }, succ: { // 下载成功 print("下载文件完成") }, fail: { code, msg in // 下载失败 print("下载文件失败:code:\\(code), msg: \\(msg)") }) } else { // 文件已存在 print("文件已存在:\\(filePath)") } print("文件信息:uuid: \\(uuid), filename: \\(filename), fileSize: \\(fileSize), filePath: \\(filePath)") }}- (void)onRecvNewMessage:(V2TIMMessage *)msg { if (msg.elemType == V2TIM_ELEM_TYPE_FILE) { V2TIMFileElem *fileElem = msg.fileElem; // 文件 ID,内部标识,可用于外部缓存 key NSString *uuid = fileElem.uuid; // 文件名称 NSString *filename = fileElem.filename; // 文件大小 int fileSize = fileElem.fileSize; // 设置文件路径,这里可以用 uuid 作为标识,避免重复下载 NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testFile%@",uuid]]; if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { // 下载文件 [fileElem downloadFile:filePath progress:^(NSInteger curSize, NSInteger totalSize) { // 下载进度 NSLog(@"%@", [NSString stringWithFormat:@"下载文件进度:curSize:%lu,totalSize:%lu",curSize,totalSize]); } succ:^{ // 下载成功 NSLog(@"下载文件完成"); } fail:^(int code, NSString *msg) { // 下载失败 NSLog(@"%@", [NSString stringWithFormat:@"下载文件失败:code:%d,msg:%@",code,msg]); }]; } else { // 文件已存在 } NSLog(@"文件信息:uuid:%@, filename:%@, fileSize:%d, filePath:%@", uuid, filename, fileSize, filePath); }}class DownloadCallback final : public V2TIMDownloadCallback {public: using SuccessCallback = std::function<void()>; using ErrorCallback = std::function<void(int, const V2TIMString&)>; using DownLoadProgressCallback = std::function<void(uint64_t, uint64_t)>; DownloadCallback() = default; ~DownloadCallback() override = default; void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback, DownLoadProgressCallback download_progress_callback) { success_callback_ = std::move(success_callback); error_callback_ = std::move(error_callback); download_progress_callback_ = std::move(download_progress_callback); } void OnSuccess() override { if (success_callback_) { success_callback_(); } } void OnError(int error_code, const V2TIMString& error_message) override { if (error_callback_) { error_callback_(error_code, error_message); } } void OnDownLoadProgress(uint64_t currentSize, uint64_t totalSize) override { if (download_progress_callback_) { download_progress_callback_(currentSize, totalSize); } }private: SuccessCallback success_callback_; ErrorCallback error_callback_; DownLoadProgressCallback download_progress_callback_;};class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的文件消息 if (message.elemList.Size() == 1) { V2TIMElem* elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_FILE) { // 文件消息 auto fileElem = static_cast<V2TIMFileElem*>(elem); // 文件 ID,内部标识,可用于外部缓存 key V2TIMString uuid = fileElem->uuid; // 文件显示名称 V2TIMString filename = fileElem->filename; // 文件大小 uint64_t fileSize = fileElem->fileSize; // 设置文件路径,这里可以用 uuid 作为标识,避免重复下载 std::filesystem::path filePath = u8"./File/File/"s + uuid.CString(); // 判断 filePath 下有没有已经下载过的文件 if (!std::filesystem::exists(filePath)) { std::filesystem::create_directories(filePath.parent_path()); auto callback = new DownloadCallback{}; callback->SetCallback( [=]() { // 下载完成 delete callback; }, [=](int error_code, const V2TIMString& error_message) { // 下载失败 delete callback; }, [=](uint64_t currentSize, uint64_t totalSize) { // 下载进度回调:已下载大小 currentSize; 总文件大小 totalSize }); fileElem->DownloadFile(filePath.string().c_str(), callback); } else { // 文件已存在 } } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

26、地理位置消息

接收到地理位置消息后,接收方可直接从 V2TIMLocationElem 中解析出经纬度信息。
示例代码向你演示如何从 V2TIMMessage 中解析出地理位置消息内容:

JavaSwiftObjective-CC++public void onRecvNewMessage(V2TIMMessage msg) { if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_LOCATION) { // 地理位置消息 V2TIMLocationElem v2TIMLocationElem = msg.getLocationElem(); // 地理位置信息描述 String desc = v2TIMLocationElem.getDesc(); // 经度 double longitude = v2TIMLocationElem.getLongitude(); // 纬度 double latitude = v2TIMLocationElem.getLatitude(); }}func onRecvNewMessage(_ msg: V2TIMMessage) { if msg.elemType == .V2TIM_ELEM_TYPE_LOCATION { let locationElem = msg.locationElem // 地理位置信息描述 let desc = locationElem?.desc // 经度 let longitude = locationElem?.longitude // 纬度 let latitude = locationElem?.latitude print("地理位置信息:desc:\\(desc), longitude:\\(longitude), latitude:\\(latitude)") }}- (void)onRecvNewMessage:(V2TIMMessage *)msg { if (msg.elemType == V2TIM_ELEM_TYPE_LOCATION) { V2TIMLocationElem *locationElem = msg.locationElem; // 地理位置信息描述 NSString *desc = locationElem.desc; // 经度 double longitude = locationElem.longitude; // 纬度 double latitude = locationElem.latitude; NSLog(@"地理位置信息:desc:%@, longitude:%f, latitude:%f", desc, longitude, latitude); }}class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的地理位置消息 if (message.elemList.Size() == 1) { V2TIMElem* elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_LOCATION) { // 地理位置消息 auto locationElem = static_cast<V2TIMLocationElem*>(elem); // 地理位置描述信息 V2TIMString desc = locationElem->desc; // 经度,发送消息时设置 double longitude = locationElem->longitude; // 纬度,发送消息时设置 double latitude = locationElem->latitude; } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

27、表情消息

SDK 仅为表情消息提供消息透传的通道,消息内容字段参考 V2TIMFaceElem (Java / Swift / Objective-C / C++) 定义。其中 index 和 data 的内容由客户自定义。

示例:

·发送方可设置 index = 1, data = "x12345",表示 “微笑” 表情。

·接收方收到表情消息后解析出 1 和 "x12345",按照预设的规则将其展示为 “微笑” 表情。

示例代码向你演示如何从 V2TIMMessage 中解析出表情消息内容:

JavaSwiftObjective-CC++public void onRecvNewMessage(V2TIMMessage msg) { if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_FACE) { // 表情消息 V2TIMFaceElem v2TIMFaceElem = msg.getFaceElem(); // 表情所在的位置 int index = v2TIMFaceElem.getIndex(); // 表情自定义数据 byte[] data = v2TIMFaceElem.getData(); }}func onRecvNewMessage(_ msg: V2TIMMessage) { guard msg.elemType == .V2TIM_ELEM_TYPE_FACE, let faceElem = msg.faceElem else { return } // 表情所在的位置 let index = faceElem.index // 表情自定义数据 guard let data = faceElem.data else { print("表情数据为空") return } print("表情信息:index: \\(index), data: \\(data)")}- (void)onRecvNewMessage:(V2TIMMessage *)msg { if (msg.elemType == V2TIM_ELEM_TYPE_FACE) { V2TIMFaceElem *faceElem = msg.faceElem; // 表情所在的位置 int index = faceElem.index; // 表情自定义数据 NSData *data = faceElem.data; NSLog(@"表情信息:index: %d, data: %@", index, data); }}class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的表情消息 if (message.elemList.Size() == 1) { V2TIMElem* elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_FACE) { // 表情消息 auto faceElem = static_cast<V2TIMFaceElem*>(elem); // 表情所在的位置 uint32_t index = faceElem->index; // 表情自定义数据 V2TIMBuffer data = faceElem->data; } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

28、接收广播消息

你只能调用 REST API 直播群广播消息 向同一个 AppID 下面所有的直播群发送广播消息,所有直播群在线群成员均能收到此消息。
SDK 不能发送广播消息,只能接收来自于 REST API 的消息。
接收消息依然是在上文所述的 onRecvNewMessage 通知里。你可以在收到消息后,根据消息的 isBroadcastMessage 判断该消息是否是广播消息。

注意:1. 该功能仅对旗舰版客户开放,购买 旗舰版套餐包 后可使用。

注意:2. 仅增强版 SDK 6.5.2803 及以上版本支持。

注意:3. 仅直播群支持广播消息,其他类型群组暂不支持。

示例代码如下:

JavaSwiftObjective-CC++public void onRecvNewMessage(V2TIMMessage msg) { if (msg.isBroadcastMessage) { // 收到了广播消息 }}func onRecvNewMessage(_ msg: V2TIMMessage) { if msg.isBroadcastMessage { // 收到了广播消息 } }- (void)onRecvNewMessage:(V2TIMMessage *)msg { if (msg.isBroadcastMessage) { // 收到了广播消息 }}class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { if (message.isBroadcastMessage) { // 收到了广播消息 } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

29、接收多个 Elem 的消息

1. 通过 Message 对象正常解析出第一个 Elem 对象。

2. 通过第一个 Elem 对象的 nextElem 方法获取下一个 Elem 对象,如果下一个 Elem 对象存在,会返回 Elem 对象实例,如果不存在,会返回 nil/null。

示例代码如下:

JavaSwiftObjective-CC++@Overridepublic void onRecvNewMessage(V2TIMMessage msg) { // 查看第一个 Elem int elemType = msg.getElemType(); if (elemType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) { // 文本消息 V2TIMTextElem v2TIMTextElem = msg.getTextElem(); String text = v2TIMTextElem.getText(); // 查看 v2TIMTextElem 后面还有没有更多 elem V2TIMElem elem = v2TIMTextElem.getNextElem(); while (elem != null) { // 判断 elem 类型,以 V2TIMCustomElem 为例 if (elem instanceof V2TIMCustomElem) { V2TIMCustomElem customElem = (V2TIMCustomElem) elem; byte[] data = customElem.getData(); } // 继续查看当前 elem 后面还有没更多 elem elem = elem.getNextElem(); } // elem 如果为 null,表示所有 elem 都已经解析完 }}func onRecvNewMessage(_ msg: V2TIMMessage) { // 查看第一个 Elem if msg.elemType == .V2TIM_ELEM_TYPE_TEXT { let textElem = msg.textElem let text = textElem?.text print("文本信息 : \\(text)") // 查看 textElem 后面还有没更多 Elem var elem: V2TIMElem? = textElem?.getNextElem() while elem != nil { // 判断 elem 类型 if let customElem = elem as? V2TIMCustomElem { let customData = customElem.data print("自定义信息 : \\(customData)") } // 继续查看当前 elem 后面还有没更多 elem elem = elem?.getNextElem() } // elem 如果为 nil,表示所有 elem 都已经解析完 }}- (void)onRecvNewMessage:(V2TIMMessage *)msg { // 查看第一个 Elem if (msg.elemType == V2TIM_ELEM_TYPE_TEXT) { V2TIMTextElem *textElem = msg.textElem; NSString *text = textElem.text; NSLog(@"文本信息 : %@", text); // 查看 textElem 后面还有没更多 Elem V2TIMElem *elem = textElem.nextElem; while (elem != nil) { // 判断 elem 类型 if ([elem isKindOfClass:[V2TIMCustomElem class]]) { V2TIMCustomElem *customElem = (V2TIMCustomElem *)elem; NSData *customData = customElem.data; NSLog(@"自定义信息 : %@",customData); } // 继续查看当前 elem 后面还有没更多 elem elem = elem.nextElem; } // elem 如果为 nil,表示所有 elem 都已经解析完 }}class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {public: /** * 收到新消息 * * @param message 消息 */ void OnRecvNewMessage(const V2TIMMessage& message) override { // 解析出 groupID 和 userID V2TIMString groupID = message.groupID; V2TIMString userID = message.userID; // 判断当前是单聊还是群聊: // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊 // 解析出 message 中的多个 Elem 消息 // 所有的 Elem 都保存在 message.elemList 中,访问这些 Elem 需要遍历 message.elemList for (size_t i = 0; i < message.elemList.Size(); ++i) { V2TIMElem* elem = message.elemList[i]; switch (elem->elemType) { case V2TIMElemType::V2TIM_ELEM_TYPE_NONE: { // 未知消息 } break; case V2TIMElemType::V2TIM_ELEM_TYPE_TEXT: { // 文本消息 auto textElem = static_cast<V2TIMTextElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_CUSTOM: { // 自定义消息 auto customElem = static_cast<V2TIMCustomElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_IMAGE: { // 图片消息 auto imageElem = static_cast<V2TIMImageElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_SOUND: { // 语音消息 auto soundElem = static_cast<V2TIMSoundElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_VIDEO: { // 视频消息 auto videoElem = static_cast<V2TIMVideoElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_FILE: { // 文件消息 auto fileElem = static_cast<V2TIMFileElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_LOCATION: { // 地理位置消息 auto locationElem = static_cast<V2TIMLocationElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_FACE: { // 表情消息 auto faceElem = static_cast<V2TIMFaceElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_GROUP_TIPS: { // 群 Tips 消息 auto mergerElem = static_cast<V2TIMMergerElem*>(elem); } break; case V2TIMElemType::V2TIM_ELEM_TYPE_MERGER: { // 合并消息 auto groupTipsElem = static_cast<V2TIMGroupTipsElem*>(elem); } break; default: { } break; } } } // 其他成员 …};// 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调AdvancedMsgListener advancedMsgListener;V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener)

赞 (0)
上一篇 4天前
下一篇 4天前