tencent cloud

文档反馈

Android&iOS&Windows&Mac

最后更新时间:2024-09-09 20:48:47

    功能描述

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

    设置消息监听器

    SDK 提供了 2 种消息监听器,简单消息监听器 V2TIMSimpleMsgListener 和高级消息监听器 V2TIMAdvancedMsgListener。 两者的区别在于:
    1. 简单消息监听器只能接收文本、自定义消息。如果您的业务只需要这两种消息,可以仅使用简单消息监听器。
    2. 高级消息监听器可以接收所有类型的消息。如果您的业务还需要支持富媒体、合并消息等其他类型,请使用高级消息监听器。
    注意
    1. addSimpleMsgListeneraddAdvancedMsgListener 请使用其中之一,切勿混用,以免产生不可预知的逻辑 bug。
    2. 如果想要正常接收下面各种类型的消息,必须先添加消息监听器,否则无法正常接收。

    简单消息监听器

    添加监听器

    接收方调用 addSimpleMsgListener (Android / iOS & Mac / Windows) 添加简单消息监听器。一般建议在比较靠前的时间点调用,例如聊天消息界面初始化后,确保 App 能及时收到消息。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);
    // self 为 id<V2TIMSignalingListener>
    [[V2TIMManager sharedInstance] addSimpleMsgListener:self];
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    // 成员 ...
    };
    
    // 添加基本消息的事件监听器,注意在移除监听器之前需要保持 simpleMsgListener 的生命期,以免接收不到事件回调
    SimpleMsgListener simpleMsgListener;
    V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

    监听器回调事件

    添加成功简单消息监听器后,接收方可以在 V2TIMSimpleMsgListener (Android / iOS & Mac / Windows) 的回调中接收不同类型消息,说明如下:
    Android
    iOS & Mac
    Windows
    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) {}
    }
    /// 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;
    @end
    class 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 (Android / iOS & Mac / Windows) 移除简单消息监听器。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    V2TIMManager.getInstance().removeSimpleMsgListener(simpleMsgListener);
    // self 为 id<V2TIMSignalingListener>
    [[V2TIMManager sharedInstance] removeSimpleMsgListener:self];
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    // 成员 ...
    };
    
    // simpleMsgListener 是 SimpleMsgListener 的实例
    V2TIMManager::GetInstance()->RemoveSimpleMsgListener(&simpleMsgListener);

    高级消息监听器

    添加监听器

    接收方调用 addAdvancedMsgListener (Android / iOS & Mac / Windows) 添加高级消息监听器。一般建议在比较靠前的时间点调用,例如例如聊天消息界面初始化后,确保 App 能及时收到消息。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener);
    // self 为 id<V2TIMAdvancedMsgListener>
    [[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    // 成员 ...
    };
    
    // 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    监听器回调事件

    添加成功高级消息监听器后,接收方可以在 V2TIMAdvancedMsgListener (Android / iOS & Mac / Windows) 的回调中接收不同类型消息,说明如下:
    Android
    iOS & Mac
    Windows
    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) {}
    }
    /// 高级消息监听器
    @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;
    @end
    class 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 (Android / iOS & Mac / Windows) 移除高级消息监听器。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    V2TIMManager.getMessageManager().removeAdvancedMsgListener(advancedMsgListener);
    // self 为 id<V2TIMAdvancedMsgListener>
    [[V2TIMManager sharedInstance] removeAdvancedMsgListener:self];
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    // 成员 ...
    };
    
    // advancedMsgListener 是 AdvancedMsgListener 的实例
    V2TIMManager::GetInstance()->GetMessageManager()->RemoveAdvancedMsgListener(&advancedMsgListener);

    接收文本消息

    使用简单消息监听器接收

    单聊文本消息

    接收方使用简单消息监听器接收单聊文本消息,需要以下几步:
    1. 调用 addSimpleMsgListener 设置事件监听器。
    2. 监听 onRecvC2CTextMessage (Android / iOS & Mac / Windows) 回调,在其中接收文本消息。
    3. 希望停止接收消息,调用 removeSimpleMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。
    代码示例如下:
    Android
    iOS & Mac
    Windows
    // 设置事件监听器
    V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);
    
    // 接收单聊文本消息
    /**
    * 收到 C2C 文本消息
    *
    * @param msgID 消息唯一标识
    * @param sender 发送方信息
    * @param text 发送内容
    */
    public void onRecvC2CTextMessage(String msgID, V2TIMUserInfo sender, String text) {
    // 可解析消息并展示到 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 (Android / iOS & Mac / Windows) 回调,在其中接收文本消息。
    3. 希望停止接收消息,调用 removeSimpleMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。
    代码示例如下:
    Android
    iOS & Mac
    Windows
    // 设置事件监听器
    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 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 (Android / iOS & Mac / Windows) 回调,在其中接收文本消息。
    3. 希望停止接收消息,调用 removeAdvancedMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。
    代码示例如下:
    Android
    iOS & Mac
    Windows
    // 设置事件监听器
    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 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 (Android / iOS & Mac / Windows) 回调,在其中接收单聊自定义消息。
    3. 希望停止接收消息,调用 removeSimpleMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。
    代码示例如下:
    Android
    iOS & Mac
    Windows
    /**
    * 接收单聊自定义消息
    * @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 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 (Android / iOS & Mac / Windows) 回调,在其中接收群聊自定义消息。
    3. 希望停止接收消息,调用 removeSimpleMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。
    Android
    iOS & Mac
    Windows
    /**
    * 接收群聊自定义消息
    * @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 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);

    使用高级消息监听器接收

    接收方使用高级消息监听器接收单聊、群聊自定义消息,需要以下几步:
    1. 调用 addAdvancedMsgListener 设置事件监听器。
    2. 监听 onRecvNewMessage (Android / iOS & Mac / Windows) 回调,在其中接收自定义消息。
    3. 希望停止接收消息,调用 removeAdvancedMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。
    代码示例如下:
    Android
    iOS & Mac
    Windows
    // 设置事件监听器
    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 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);

    接收富媒体消息

    接收富媒体消息只能使用高级消息监听器,需要以下几步:
    1. 接收方调用 addAdvancedMsgListener 接口设置高级消息监听。
    2. 接收方通过监听回调 onRecvNewMessage (Android / iOS & Mac / Windows) 获取消息 V2TIMMessage。
    3. 接收方解析 V2TIMMessage 消息中的 elemType 属性,并根据其类型进行二次解析,获取消息内部 Elem 中的具体内容。
    4. 希望停止接收消息,调用 removeAdvancedMsgListener 移除监听。该步骤不是必须的,客户可以按照业务需求调用。

    图片消息

    一个图片消息会包含三种格式大小的图片,分别为原图、大图、微缩图(SDK 会在发送图片消息的时候自动生成微缩图、大图,客户不需要关心):
    大图:将原图等比压缩。压缩后宽、高中较小的一个等于 720 像素。
    缩略图:将原图等比压缩。压缩后宽、高中较小的一个等于 198 像素。
    接收端收到图片消息后,我们推荐您调用 SDK 的 downloadImage (Android / iOS & Mac / Windows) 将图片下载到本地,再取出图片渲染到 UI 层。
    为了避免重复下载,节省资源,我们推荐您将 V2TIMImage 对象的 uuid 属性值设置到图片的下载路径中,作为图片的标识。
    示例代码向您演示如何从 V2TIMMessage 中解析出图片消息内容:
    Android
    iOS & Mac
    Windows
    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 {
    // 图片已存在
    }
    }
    }
    }
    - (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);

    视频消息

    接收方收到视频消息后,一般需要在聊天界面显示一个视频预览图,当用户点击消息后,才会触发视频的播放。 所以这里需要两步:
    1. 下载视频截图。我们推荐您调用 SDK 的 downloadSnapshot (Android / iOS & Mac / Windows) 进行下载。
    2. 下载视频。我们推荐您调用 SDK 的 downloadVideo (Android / iOS & Mac / Windows) 进行下载。
    为了避免重复下载,节省资源,我们推荐您将 V2TIMVideoElem 对象的 videoUUID 属性值设置到视频的下载路径中,作为视频的标识。
    示例代码向您演示如何从 V2TIMMessage 中解析出视频消息内容:
    Android
    iOS & Mac
    Windows
    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 {
    // 文件已存在
    }
    }
    }
    - (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);

    语音消息

    接收端收到语音消息后,我们推荐您调用 SDK 的 downloadSound (Android / iOS & Mac / Windows) 将语音下载到本地,再获取本地语音文件播放。
    为了避免重复下载,节省资源,我们推荐您将 V2TIMSoundElem 对象的 uuid 属性值设置到语音的下载路径中,作为语音的标识。
    示例代码向您演示如何从 V2TIMMessage 中解析出语音消息内容:
    Android
    iOS & Mac
    Windows
    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 {
    // 文件已存在
    }
    }
    }
    - (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);

    文件消息

    接收端收到文件消息后,我们推荐您调用 SDK 的 downloadFile (Android / iOS & Mac / Windows) 将文件下载到本地,再获取本地文件展示。
    为了避免重复下载,节省资源,我们推荐您将 V2TIMFileElem 对象的 uuid 属性值设置到文件的下载路径中,作为文件的标识。
    示例代码向您演示如何从 V2TIMMessage 中解析出文件消息内容:
    Android
    iOS & Mac
    Windows
    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 {
    // 文件已存在
    }
    }
    }
    - (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);

    地理位置消息

    接收到地理位置消息后,接收放可直接从 V2TIMLocationElem 中解析出经纬度信息。 示例代码向您演示如何从 V2TIMMessage 中解析出地理位置消息内容:
    Android
    iOS & Mac
    Windows
    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();
    }
    }
    - (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);

    表情消息

    SDK 仅为表情消息提供消息透传的通道,消息内容字段参考 V2TIMFaceElem (Android / iOS & Mac / Windows) 定义。其中 index 和 data 的内容由客户自定义。
    例如发送方可设置 index = 1, data = "x12345",表示 “微笑” 表情。 接收方收到表情消息后解析出 1 和 "x12345",按照预设的规则将其展示为 “微笑” 表情。
    示例代码向您演示如何从 V2TIMMessage 中解析出表情消息内容:
    Android
    iOS & Mac
    Windows
    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();
    }
    }
    - (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);

    接收广播消息

    您只能调用 REST API 直播群广播消息 向同一个 AppID 下面所有的直播群发送广播消息,所有直播群在线群成员均能收到此消息。
    SDK 不能发送广播消息,只能接收来自于 REST API 的消息。
    接收消息依然是在上文所述的 onRecvNewMessage 通知里。您可以在收到消息后,根据消息的 isBroadcastMessage 判断该消息是否是广播消息。
    注意:
    1. 该功能仅对进阶版客户开放,购买进阶版后可使用。
    2. 仅增强版 SDK 6.5.2803 及以上版本支持。
    3. 仅直播群支持广播消息,其他类型群组暂不支持。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    public void onRecvNewMessage(V2TIMMessage msg) {
    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);

    接收多个 Elem 的消息

    1. 通过 Message 对象正常解析出第一个 Elem 对象。
    2. 通过第一个 Elem 对象的 nextElem 方法获取下一个 Elem 对象,如果下一个 Elem 对象存在,会返回 Elem 对象实例,如果不存在,会返回 nil/null。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    @Override
    public 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 都已经解析完
    }
    }
    - (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);
    
    联系我们

    联系我们,为您的业务提供专属服务。

    技术支持

    如果你想寻求进一步的帮助,通过工单与我们进行联络。我们提供7x24的工单服务。

    7x24 电话支持