收发消息

更新时间: 2025/01/15 15:20:22

网易云信即时通讯 SDK(NetEase IM SDK,简称 NIM SDK)支持收发多种消息类型,助您快速实现多样化的消息业务场景。

本文介绍通过 NIM SDK 实现消息收发的具体流程。

本文介绍的消息收发仅包含单聊、高级群、超大群消息,不包含聊天室、圈组的消息,具体实现流程请分别参考 聊天室消息收发圈组消息收发

支持平台

本文内容适用的开发平台或框架如下表所示:

Android iOS macOS/Windows Web/uni-app/小程序 Node.js/Electron HarmonyOS Flutter
✔️️️️ ✔️️️️ ✔️️️️ ✔️️️️ ✔️️️️ ✔️️️️ ✔️️️️

技术原理

应用集成 NIM SDK 并完成 SDK 初始化后,消息收发流程如下图所示(本地提示消息通知消息 除外):

sequenceDiagram
autoNumber
participant ne as 网易云信 IM 服务端
participant clienta as 客户端 A
participant app as 应用服务端
participant clientb as 客户端 B

app->>ne:传入应用账号
ne->>app:返回 Token
clienta->>app:登录
clientb->>app:登录
app->>clienta:返回 Token
app->>clientb:返回 Token
clienta->>ne:带 Token 登录 IM
clientb->>ne:带 Token 登录 IM

par 收发消息
clienta->>ne: 收发消息
clientb->>ne: 
ne->>clienta: 收发消息
ne->>clientb: 
end

消息收发基本流程如下:

  1. 账号集成与登录。
    1. 开发者将用户账号 accountId 传入网易云信 IM 服务器。
    2. 网易云信 IM 服务器返回 Token 给应用服务器。
    3. 应用客户端登录应用服务器。
    4. 应用服务器将 Token 返回给应用客户端。
    5. 用户携带用户账号和 Token 登录网易云信 IM 服务器,网易云信 IM 服务器进行校验。
  2. 用户 A 发送一条消息到网易云信 IM 服务器。
  3. 网易云信 IM 服务器投递消息至其他用户,分为如下两种情况:
    • 如果为单聊消息,IM 服务器将其投递至用户 B。
    • 如果为群聊消息,IM 服务器将其投递至群内其他每一位用户。

上图仅以静态 Token 登录为例展示消息收发流程。网易云信 IM 还支持动态 Token 登录鉴权和第三方回调登录鉴权,相关详情请参考 登录鉴权

前提条件

在实现消息收发之前,请确保:

频控限制

发送消息(sendMessage)方法一分钟内默认最多可调用 300 次。

收发文本消息

API 调用时序

sequenceDiagram

par 步骤 1:初始化 SDK
发送方 ->> NIM: 初始化 SDK
接收方 ->> NIM: 初始化 SDK
end
par 步骤 2:发送方登录
发送方 ->> NIM: 登录
end
par 步骤 3:接收方注册消息监听并登录
接收方 ->> NIM: 监听消息接收<br>(addMessageListener)
接收方 ->> NIM: 登录
end
par 步骤 4:消息收发
发送方 ->> NIM: 构造文本消息<br>(createTextMessage)
发送方 ->> NIM: 发送消息<br>(sendMessage)
NIM ->> 接收方: 投递消息体
end

实现步骤

  1. 接收方 注册消息监听器,监听消息接收回调事件。

    Android/iOS/macOS/Windows

    接收方调用 addMessageListener 方法注册消息监听器,监听消息接收回调事件 onReceiveMessages

    Android
    JavaV2NIMMessageService v2MessageService = NIMClient.getService(V2NIMMessageService.class);
    
    V2NIMMessageListener messageListener = new V2NIMMessageListener() {
    
        @Override
        public void onReceiveMessages(List<V2NIMMessage> messages) {
    
        }
    };
    v2MessageService.addMessageListener(messageListener);
    
    iOS
    Objective-C[[[NIMSDK sharedSDK] v2MessageService] addMessageListener:listener];
    
    macOS/Windows
    C++V2NIMMessageListener listener;
    listener.onReceiveMessages = [](nstd::vector<V2NIMMessage> messages) {
        // receive messages
    };
    messageService.addMessageListener(listener);
    

    如需移除消息监听器,可调用 removeMessageListener

    Android
    JavaV2NIMMessageService v2MessageService = NIMClient.getService(V2NIMMessageService.class);
    
    v2MessageService.removeMessageListener(messageListener);
    
    iOS
    Objective-C[[[NIMSDK sharedSDK] v2MessageService] removeMessageListener:listener];
    
    macOS/Windows
    C++messageService.removeMessageListener(listener);
    

    Web/uni-app/小程序/NOde.js/Electron/HarmonyOS

    调用 on("EventName") 方法注册消息监听器,监听消息接收回调事件 onReceiveMessages

    Web/uni-app/小程序
    TypeScriptnim.V2NIMMessageService.on("onReceiveMessages", function (messages: V2NIMMessage[]) {})
    

    如需移除登录相关监听器,可调用 off("EventName")

    TypeScriptnim.V2NIMMessageService.off("onReceiveMessages", function (messages: V2NIMMessage[]) {})
    
    Node.js/Electron
    TypeScriptv2.messageService.on("receiveMessages", function (messages: V2NIMMessage[]) {})
    

    如需移除登录相关监听器,可调用 off("EventName")

    TypeScriptv2.messageService.off("receiveMessages", function (messages: V2NIMMessage[]) {})
    
    HarmonyOS
    TypeScriptnim.messageService.on("onReceiveMessages", function (messages: V2NIMMessage[]) {})
    

    如需移除登录相关监听器,可调用 off("EventName")

    TypeScriptnim.messageService.off("onReceiveMessages", function (messages: V2NIMMessage[]) {})
    

    Flutter

    调用 add 方法注册消息监听器,监听消息接收回调事件 onReceiveMessages

    Dartsubsriptions.add(
        NimCore.instance.messageService.onReceiveMessages.listen((event) {
        //do something
        }));
    

    如需移除登录相关监听器,可调用 cancel

    Dartsubsriptions.forEach((subsription) {
    subsription.cancel();
    });
    
  2. 发送方 调用 createTextMessage 方法,构建一条文本消息。

    Android
    JavaV2NIMMessage v2TextMessage = V2NIMMessageCreator.createTextMessage("text content");
    
    iOS
    Objective-CV2NIMMessage *v2Message = [V2NIMMessageCreator createTextMessage:@"hello world"];
    
    macOS/Windows
    C++auto textMessage = V2NIMMessageCreator::createTextMessage("hello world");
    if(!textMessage) {
        // create text message failed
    }
    
    Web/uni-app/小程序
    TypeScripttry {
        const message = nim.V2NIMMessageCreator.createTextMessage('hello world')
    } catch(err) {
        // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
        const message = v2.messageCreator.createTextMessage('Hello, world!')
    } catch(err) {
        // todo error
    }
    
    HarmonyOS
    TypeScripttry {
        const message = nim.messageCreator.createTextMessage('hello world')
    } catch(err) {
        // todo error
    }
    
    Flutter
    Dartawait MessageCreator.createTextMessage(text);
    
  3. 发送方 调用 sendMessage 方法,发送已构建的文本消息。

    发送消息时,设置消息发送成功回调参数 success 和消息发送失败回调参数 failure,监听消息发送是否成功。若消息发送成功,则通过成功回调返回发送消息结果(接收消息对象)。若消息发送失败,则通过失败回调获取相关错误码。

    消息发送配置属性说明如下:

    名称 说明
    messageConfig 消息相关配置
    routeConfig 消息路由(抄送)相关配置
    pushConfig 消息第三方推送相关配置
    antispamConfig 消息反垃圾相关配置,包括本地反垃圾或安全通配置
    robotConfig 消息机器人相关配置

    示例代码如下:

    Android
    JavaV2NIMMessageService v2MessageService = NIMClient.getService(V2NIMMessageService.class);
    // 创建一条文本消息
    V2NIMMessage v2Message = V2NIMMessageCreator.createTextMessage("xxx");
    // 以单聊类型为例
    String conversationId = V2NIMConversationIdUtil.conversationId("xxx", V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P);
    // 根据实际情况配置
    V2NIMMessageAntispamConfig antispamConfig = V2NIMMessageAntispamConfig.V2NIMMessageAntispamConfigBuilder.builder()
    .withAntispamBusinessId()
    .withAntispamCheating()
    .withAntispamCustomMessage()
    .withAntispamEnabled()
    .withAntispamExtension()
    .build();
    
    // 根据实际情况配置
    V2NIMMessageConfig messageConfig = V2NIMMessageConfig.V2NIMMessageConfigBuilder.builder()
    .withLastMessageUpdateEnabled()
    .withHistoryEnabled()
    .withOfflineEnabled()
    .withOnlineSyncEnabled()
    .withReadReceiptEnabled()
    .withRoamingEnabled()
    .withUnreadEnabled()
    .build();
    // 根据实际情况配置
    V2NIMMessagePushConfig pushConfig = V2NIMMessagePushConfig.V2NIMMessagePushConfigBuilder.builder()
    .withContent()
    .withForcePush()
    .withForcePushAccountIds()
    .withForcePushContent()
    .withPayload()
    .withPushEnabled()
    .withPushNickEnabled()
    .build();
    // 根据实际情况配置
    V2NIMMessageRobotConfig robotConfig = V2NIMMessageRobotConfig.V2NIMMessageRobotConfigBuilder.builder()
    .withAccountId()
    .withCustomContent()
    .withFunction()
    .withTopic()
    .build();
    // 根据实际情况配置
    V2NIMMessageRouteConfig routeConfig = V2NIMMessageRouteConfig.V2NIMMessageRouteConfigBuilder.builder()
    .withRouteEnabled()
    .withRouteEnvironment()
    .build();
    // 根据实际情况配置
    V2NIMSendMessageParams sendMessageParams = V2NIMSendMessageParams.V2NIMSendMessageParamsBuilder.builder()
    .withAntispamConfig(antispamConfig)
    .withClientAntispamEnabled()
    .withClientAntispamReplace()
    .withMessageConfig(messageConfig)
    .withPushConfig(pushConfig)
    .withRobotConfig(robotConfig)
    .withRouteConfig(routeConfig)
    .build();
    // 发送消息
    v2MessageService.sendMessage(v2Message, conversationId, sendMessageParams,
    new V2NIMSuccessCallback<V2NIMSendMessageResult>() {
        @Override
        public void onSuccess(V2NIMSendMessageResult v2NIMSendMessageResult) {
            // TODO: 发送成功
            }
        },
        new V2NIMFailureCallback() {
            @Override
            public void onFailure(V2NIMError error) {
                // TODO: 发送失败
            }
        });
    }
    
    iOS
    Objective-C// 创建一条文本消息
    V2NIMMessage *message = [V2NIMMessageCreator createTextMessage:@"v2 message"];
    V2NIMSendMessageParams *params = [[V2NIMSendMessageParams alloc] init];
    // 发送消息
    [[[NIMSDK sharedSDK] v2MessageService] sendMessage:message
                                        conversationId:@"conversationId"
                                                params:params
                                            success:^(V2NIMSendMessageResult * _Nonull result) {
                                                // 发送成功回调
                                                }
                                            failure:^(V2NIMError * _Nonnull error) {
                                                // 发送失败回调,error 包含错误原因
                                                }
    }];
    
    macOS/Windows
    C++// 以单聊类型为例
    auto conversationId = V2NIMConversationIdUtil::p2pConversationId("target_account_id");
    // 创建一条文本消息
    auto message = V2NIMMessageCreator::createTextMessage("hello world");
    auto params = V2NIMSendMessageParams();
    // 发送消息
    messageService.sendMessage(
        message,
        conversationId,
        params,
        [](V2NIMSendMessageResult result) {
            // send message succeeded
        },
        [](V2NIMError error) {
            // send message failed, handle error
        });
    
    Web/uni-app/小程序
    TypeScripttry {
    // 创建一条文本消息
    const message: V2NIMMessage = nim.V2NIMMessageCreator.createTextMessage("hello")
    // 发送消息
    const res: V2NIMSendMessageResult = await nim.V2NIMMessageService.sendMessage(message, 'test1|1|test2')
    // Update UI with success message.
    } catch (err) {
    // todo error
    }
    
    Node.js/Electron
    TypeScript// 创建一条文本消息
    const message = v2.messageCreator.createTextMessage('Hello NTES IM')
    // 发送消息
    const result = await v2.messageService.sendMessage(message, conversationId, params, progressCallback)
    
    HarmonyOS
    TypeScripttry {
    // 创建一条文本消息
    const message: V2NIMMessage = nim.messageCreator.createTextMessage("hello")
    // 发送消息
    const res: V2NIMSendMessageResult = await nim.messageService.sendMessage(message, 'test1|1|test2')
    // todo Success
    } catch (err) {
    // todo error
    }
    
    Flutter
    Dartawait NimCore.instance.messageService.sendMessage(message, conversationId, params);
    
  4. 接收方 通过 onReceiveMessages 回调收到文本消息。

收发富媒体消息

富媒体消息包括图片消息、语音消息、视频消息和文件消息。

API 调用时序

sequenceDiagram

par 步骤 1:初始化 SDK
发送方 ->> NIM: 初始化 SDK
接收方 ->> NIM: 初始化 SDK
end
par 步骤 2:发送方登录
发送方 ->> NIM: 登录
end
par 步骤 3:接收方注册消息监听并登录
接收方 ->> NIM: 监听消息接收<br>(addMessageListener)
接收方 ->> NIM: 登录
end
par 步骤 4:消息收发
发送方 ->> NIM: 构建富媒体消息
发送方 ->> NIM: 发送消息(可通过回调监听消息附件上传进度)
发送方 ->> NIM: (可选)取消上传附件<br>(cancelMessageAttachmentUpload)
NIM ->> 接收方: 投递消息体
end

实现步骤

  1. 接收方 注册消息监听器,监听消息接收回调事件。

    示例代码请参考 实现文本消息收发 的实现步骤 1。

  2. 发送方 构建富媒体消息,指定 NOS 存储场景。

    NIM SDK 支持设置 IM 数据在网易对象存储(Netease Object Storage,简称 NOS)服务上的存储场景。这里的存储场景指需要存储到 NOS 的 IM 数据类型,默认包括多媒体消息的附件资源和用户、群组的资料,且这两类 IM 数据在 NOS 的存储默认不过期。NIM SDK 也支持指定 IM 数据在 NOS 上的过期时间,过期后,数据将被 NOS 删除。

  • 不同消息类型创建方法

    消息类型
    创建方法
    图片消息 createImageMessage
    语音消息 createAudioMessage
    视频消息 createVideoMessage
    文件消息 createFileMessage
  • NOS 场景参数说明

    NOS 场景 说明
    默认场景 用于用户、群组资料的上传场景。
    IM 场景 用于文件的上传场景。
    系统场景 用于 SDK 内部文件的上传场景。
    安全链接场景 查看该场景的上传文件需要密钥。
    自定义场景 用户调用 addCustomStorageScene 添加的自定义场景。
  • 示例代码

    图片消息:

    Android
    JavaV2NIMMessage v2ImageMessage = V2NIMMessageCreator.createImageMessage(imagePath, name, sceneName, width, height);
    
    iOS
    Objective-CNSString *imagePath = @"文件沙盒路径";
    V2NIMMessage *message = [V2NIMMessageCreator createImageMessage:imagePath
                                                            name:@"imageName"
                                                        sceneName:@"nim_default_im"
                                                            width:200
                                                            height:200];
    
    macOS/Windows
    C++auto imageMessage = V2NIMMessageCreator::createImageMessage("imagePath", "imageName", V2NIM_STORAGE_SCENE_NAME_DEFAULT_IM, 100,
    100); if(!imageMessage) {
        // create image message failed
    }
    
    Web/uni-app/小程序
    TypeScripttry {
        const message = nim.V2NIMMessageCreator.createImageMessage(document.getElementById('fileInputId').files[0])
    } catch(err) {
        // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
        const message = v2.messageCreator.createImageMessage(imagePath, name, sceneName, width, height)
    } catch(err) {
        // todo error
    }
    
    HarmonyOS
    TypeScripttry {
        const imagePath = "沙盒路径"
        const message = nim.messageCreator.createImageMessage(imagePath)
    } catch(err) {
        // todo error
    }
    
    Flutter
    Dartawait MessageCreator.createImageMessage(imagePath, name, sceneName, width, height);
    

    语音消息:

    Android
    JavaV2NIMMessage v2AudioMessage = V2NIMMessageCreator.createAudioMessage(audioPath, name, sceneName, duration);
    
    iOS
    Objective-CNSString *audioPath = @"文件沙盒路径";
    V2NIMMessage *message = [V2NIMMessageCreator createAudioMessage:audioPath
                                                            name:@"audioName"
                                                        sceneName:@"nim_default_im"
                                                        duration:2];
    
    macOS/Windows
    C++auto audioMessage = V2NIMMessageCreator::createAudioMessage("audioPath", "audioName", V2NIM_STORAGE_SCENE_NAME_DEFAULT_IM, 100);
        if(!audioMessage) {
            // create audio message failed
    }
    
    Web/uni-app/小程序
    TypeScripttry {
        const message = nim.V2NIMMessageCreator.createAudioMessage(document.getElementById('fileInputId').files[0])
    } catch(err) {
        // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
        const message = v2.messageCreator.createAudioMessage(audioPath, name, sceneName, duration)
    } catch(err) {
        // todo error
    }
    
    HarmonyOS
    TypeScripttry {
        const audioPath = "文件沙盒路径" ;
        const message = nim.messageCreator.createAudioMessage(audioPath)
    } catch(err) {
        // todo error
    }
    
    Flutter
    Dartawait MessageCreator.createAudioMessage(audioPath, name, sceneName, duration);
    

    视频消息:

    Android
    JavaV2NIMMessage v2VideoMessage = V2NIMMessageCreator.createVideoMessage(videoPath, name, sceneName, duration, width, height);
    
    iOS
    Objective-CNSString *videoPath = @"文件沙盒路径";
    V2NIMMessage *message = [V2NIMMessageCreator createVideoMessage:videoPath
                                                            name:@"name"
                                                        sceneName:@"nim_default_im"
                                                        duration:15
                                                            width:200
                                                            height:200];
    
    macOS/Windows
    C++auto videoMessage = V2NIMMessageCreator::createVideoMessage("videoPath", "videoName", V2NIM_STORAGE_SCENE_NAME_DEFAULT_IM, 100, 100, 100);
    if (!videoMessage) {
        // create video message failed
    }
    
    Web/uni-app/小程序
    TypeScripttry {
        const message = nim.V2NIMMessageCreator.createVideoMessage(document.getElementById('fileInputId').files[0])
    } catch(err) {
        // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
        const message = v2.messageCreator.createVideoMessage(videoPath, name, sceneName, duration, width, height)
    } catch(err) {
        // todo error
    }
    
    HarmonyOS
    TypeScripttry {
        const videoPath = '沙盒路径'
        const message = nim.messageCreator.createVideoMessage(videoPath)
    } catch(err) {
        // todo error
    }
    
    Flutter
    Dartawait MessageCreator.createVideoMessage(videoPath, name, sceneName, duration, width, height);
    

    文件消息:

    Android
    JavaV2NIMMessage v2FileMessage = V2NIMMessageCreator.createFileMessage(filePath, name, sceneName);
    
    iOS
    Objective-CNSString *filePath = @"文件沙盒路径";
    V2NIMMessage *message = [V2NIMMessageCreator createFileMessage:filePath
                                                            name:@"name"
                                                        sceneName:@"nim_default_im"];
    
    macOS/Windows
    C++auto fileMessage = V2NIMMessageCreator::createFileMessage("filePath", "fileName", V2NIM_STORAGE_SCENE_NAME_DEFAULT_IM);
    if(!fileMessage) {
        // create file message failed
    }
    
    Web/uni-app/小程序
    TypeScripttry {
        const message = nim.V2NIMMessageCreator.createFileMessage(document.getElementById('fileInputId').files[0])
    } catch(err) {
        // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
        const message = v2.messageCreator.createFileMessage(filePath, name, sceneName)
    } catch(err) {
        // todo error
    }
    
    HarmonyOS
    TypeScripttry {
        const filePath = '沙盒路径'
        const message = nim.messageCreator.createFileMessage(filePath)
    } catch(err) {
        // todo error
    }
    }
    
    Flutter
    Dartawait MessageCreator.createFileMessage(filePath, name, sceneName);
    
  1. 发送方 调用 sendMessage 方法,发送已构建的富媒体消息。

    发送消息时,设置消息发送成功回调参数 success、消息发送失败参数 failure 及附件上传进度回调参数 progress,监听消息发送是否成功及附件上传进度。若消息发送成功,则通过发送成功回调返回发送消息的结果(接收消息对象)。若消息发送失败,则通过失败回调获取相关错误码。

    如果同一个图片/文件消息需要多次发送时,建议控制时序依次发送,避免出现发送失败的问题。

    • 示例代码

    以单聊消息为例,发送富媒体消息的示例代码如下:

    Android
    JavaV2NIMMessageService v2MessageService = NIMClient.getService(V2NIMMessageService.class);
    // 以单聊类型为例
    String conversationId = V2NIMConversationIdUtil.conversationId("xxx", V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P);
    // 根据实际情况配置
    V2NIMMessageAntispamConfig antispamConfig = V2NIMMessageAntispamConfig.V2NIMMessageAntispamConfigBuilder.builder()
    .withAntispamBusinessId()
    .withAntispamCheating()
    .withAntispamCustomMessage()
    .withAntispamEnabled()
    .withAntispamExtension()
    .build();
    
    // 根据实际情况配置
    V2NIMMessageConfig messageConfig = V2NIMMessageConfig.V2NIMMessageConfigBuilder.builder()
    .withLastMessageUpdateEnabled()
    .withHistoryEnabled()
    .withOfflineEnabled()
    .withOnlineSyncEnabled()
    .withReadReceiptEnabled()
    .withRoamingEnabled()
    .withUnreadEnabled()
    .build();
    // 根据实际情况配置
    V2NIMMessagePushConfig pushConfig = V2NIMMessagePushConfig.V2NIMMessagePushConfigBuilder.builder()
    .withContent()
    .withForcePush()
    .withForcePushAccountIds()
    .withForcePushContent()
    .withPayload()
    .withPushEnabled()
    .withPushNickEnabled()
    .build();
    // 根据实际情况配置
    V2NIMMessageRobotConfig robotConfig = V2NIMMessageRobotConfig.V2NIMMessageRobotConfigBuilder.builder()
    .withAccountId()
    .withCustomContent()
    .withFunction()
    .withTopic()
    .build();
    // 根据实际情况配置
    V2NIMMessageRouteConfig routeConfig = V2NIMMessageRouteConfig.V2NIMMessageRouteConfigBuilder.builder()
    .withRouteEnabled()
    .withRouteEnvironment()
    .build();
    // 根据实际情况配置
    V2NIMSendMessageParams sendMessageParams = V2NIMSendMessageParams.V2NIMSendMessageParamsBuilder.builder()
    .withAntispamConfig(antispamConfig)
    .withClientAntispamEnabled()
    .withClientAntispamReplace()
    .withMessageConfig(messageConfig)
    .withPushConfig(pushConfig)
    .withRobotConfig(robotConfig)
    .withRouteConfig(routeConfig)
    .build();
    // 发送消息
    v2MessageService.sendMessage(v2Message, conversationId, sendMessageParams,
        new V2NIMSuccessCallback<V2NIMSendMessageResult>() {
            @Override
            public void onSuccess(V2NIMSendMessageResult v2NIMSendMessageResult) {
                // TODO: 发送成功
            }
        },
        new V2NIMFailureCallback() {
            @Override
            public void onFailure(V2NIMError error) {
                // TODO: 发送失败
            }
        },
        new V2NIMProgressCallback() {
            @Override
            public void onProgress(int progress) {
                // TODO: 发送进度
            }
        });
    }
    
    iOS
    Objective-CV2NIMSendMessageParams *params = [[V2NIMSendMessageParams alloc] init];
    // 发送消息
    [[[NIMSDK sharedSDK] v2MessageService] sendMessage:message
                                        conversationId:@"conversationId"
                                                params:params
                                            success:^(V2NIMSendMessageResult * _Nonull result) {
                                                // 发送成功回调
                                                }
                                            failure:^(V2NIMError * _Nonnull error) {
                                                // 发送失败回调,error 包含错误原因
                                                }
                                            progress:^(NSUInteger) {
                                                // 发送进度
    }];
    
    macOS/Windows
    C++// 以单聊类型为例
    auto conversationId = V2NIMConversationIdUtil::p2pConversationId("target_account_id");
    auto params = V2NIMSendMessageParams();
    // 发送消息
    messageService.sendMessage(
        message,
        conversationId,
        params,
        [](V2NIMSendMessageResult result) {
            // send message succeeded
        },
        [](V2NIMError error) {
            // send message failed, handle error
        },
        [](uint32_t progress) {
            // upload progress
        });
    
    Web/uni-app/小程序
    TypeScripttry {
    // 发送消息
    const res: V2NIMSendMessageResult = await nim.V2NIMMessageService.sendMessage(message, 'test1|1|test2')
    // todo Success
    } catch (err) {
    // todo error
    }
    
    Node.js/Electron
    TypeScriptconst result = await v2.messageService.sendMessage(message, conversationId, params, progressCallback)
    
    HarmonyOS
    TypeScripttry {
    const res: V2NIMSendMessageResult = await nim.messageService.sendMessage(message, 'test1|1|test2')
    // todo Success
    } catch (err) {
    // todo error
    }
    
    Flutter
    Dartawait NimCore.instance.messageService.sendMessage(message, conversationId, params);
    
  2. 接收方 通过 onReceiveMessages 回调收到富媒体消息。

    富媒体资源一般默认自动下载,具体的默认下载策略请参考下表:

    消息类型 默认资源下载策略
    图片/视频消息 SDK 在收到消息时,自动下载缩略图和封面图片
    语音消息 SDK 在收到消息时,自动下载原音频
    文件消息 SDK 默认不下载原文件
    • 如果需要下载文件消息的文件资源,可调用 downloadFile 方法手动下载(仅支持移动端)。其他类型富媒体消息的资源如自动下载失败,也可调用该方法手动重新下载。
    • 如需自主选择下载时机,需将初始化配置参数 preloadAttach 设置为 false,关闭默认资源下载策略,再在合适的时机调用 downloadFile 方法。
    • 下载完成后,通过富媒体消息对应的附件基类获取具体的附件内容,该类继承自消息附件类。

    手动下载富媒体资源示例如下:

    Android
    JavaString url = "https://www.abc.com/ttt.txt";
    // 设置文件存储位置
    String filePath = "xxx/ttt.txt";
    NIMClient.getService(V2NIMStorageService.class).downloadFile(url, filePath, new V2NIMSuccessCallback<String>() {
        @Override
        public void onSuccess(String filePath) {
            // 下载成功
        }
    }, new V2NIMFailureCallback() {
        @Override
        public void onFailure(V2NIMError error) {
            // 下载失败
        }
    }, new V2NIMProgressCallback() {
        @Override
        public void onProgress(int progress) {
            // 下载进度
        }
    });
    
    iOS
    Objective-C// 按需配置远程目录
    NSString *url = @"https://www.abc.com/ttt.txt";
    // 按需配置存储目录
    NSString *filePath = @"Document/ttt.txt";
    [[NIMSDK sharedSDK].v2StorageService downloadFile:url filePath:filePath success:^() {
        // 成功回调
    } failure:^(V2NIMError *error) {
        // 失败回调
    } progress:^(NSUInteger progress) {
        // 进度回调
    }];
    
    macOS/Windows
    C++storageService.downloadFile(
        "https://example.com/test.txt",
        "~/test.txt",
        [](const nstd::string& url) {
            // download file succeeded
        },
        [](V2NIMError error) {
            // download file failed, handle error
        },
        [](uint32_t progress) {
            // download progress
        });
    
    HarmonyOS
    Typescriptconst url = 'https://www.abc.com/ttt.txt'
    const filePath = 'sandbox path'
    
    const path = await nim.storageService.downloadFile(url, filePath, (percent) => {
    console.log(`downloadFile percent: ${percent}`)
    })
    
    Flutter
    Dartawait NimCore.instance.storageService.downloadFile(url, filePath);
    
  3. (可选)如发送图片消息、视频消息或文件消息,发送后可调用 cancelMessageAttachmentUpload 方法取消附件的上传。

    如果附件已经上传成功,操作将会失败。如果成功取消了附件的上传,对应的消息会发送失败,附件上传状态也为失败。

    Android
    JavaV2NIMMessageService v2MessageService = NIMClient.getService(V2NIMMessageService.class);
    
    v2MessageService.cancelMessageAttachmentUpload(message,
            new V2NIMSuccessCallback<Void>() {
                @Override
                public void onSuccess(Void unused) {
                // cancel succeeded
                }
            },
            new V2NIMFailureCallback() {
                @Override
                public void onFailure(V2NIMError error) {
                // cancel failed, handle error
                }
            });
    
    iOS
    Objective-C[[[NIMSDK sharedSDK] v2MessageService] cancelMessageAttachmentUpload:message
                                                                success:^{
        // cancel succeeded
    }
                                                                failure:^(V2NIMError * _Nonnull error) {
        // cancel failed, handle error
    }];
    
    macOS/Windows
    C++V2NIMMessage message;
    // more code
    messageService.cancelMessageAttachmentUpload(
        message,
        []() {
            // cancel message attachment upload succeeded
        },
        [](V2NIMError error) {
            // cancel message attachment upload failed, handle error
        });
    
    Web/uni-app/小程序
    TypeScripttry {
    await nim.V2NIMMessageService.cancelMessageAttachmentUpload(message)
    // todo Success
    } catch (err) {
    // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
    await v2.messageService.cancelMessageAttachmentUpload(message)
    // todo Success
    } catch (err) {
    // todo error
    }
    
    HarmonyOS
    TypeScripttry {
    await nim.messageService.cancelMessageAttachmentUpload(message)
    // todo Success
    } catch (err) {
    // todo error
    }
    
    Flutter
    Dartawait NimCore.instance.messageService.cancelMessageAttachmentUpload(message);
    

收发地理位置消息

地理位置消息收发流程与文本消息收发流程基本一致,区别在于构建消息的调用方法不同(需调用 createLocationMessage)。本节仅简要展示相关调用示例,具体实现流程请参考 实现文本消息收发

API 调用时序

sequenceDiagram

par 步骤 1:初始化 SDK
发送方 ->> NIM: 初始化 SDK
接收方 ->> NIM: 初始化 SDK
end
par 步骤 2:发送方登录
发送方 ->> NIM: 登录
end
par 步骤 3:接收方注册消息监听并登录
接收方 ->> NIM: 监听消息接收<br>(addMessageListener)
接收方 ->> NIM: 登录
end
par 步骤 4:消息收发
发送方 ->> NIM: 构造地理位置消息<br>(createLocationMessage)
发送方 ->> NIM: 发送消息<br>(sendMessage)
NIM ->> 接收方: 投递消息体
end

实现步骤

  1. 接收方 注册消息监听器,监听消息接收回调事件。

    示例代码请参考 实现文本消息收发 的实现步骤 1。

  2. 发送方 调用 createLocationMessage 方法,构建一条地理位置消息。

    Android
    JavaV2NIMMessage v2LocationMessage = V2NIMMessageCreator.createLocationMessage(latitude,longitude, address);
    
    iOS
    Objective-CV2NIMMessage *message = [V2NIMMessageCreator createLocationMessage:37.787359
                                                            longitude:-122.408227
                                                            address:@"杭州滨江区网商路 399 号"];
    
    macOS/Windows
    C++auto locationMessage = V2NIMMessageCreator::createLocationMessage(100, 100, "address");
    if(!locationMessage) {
        // create location message failed
    }
    
    Web/uni-app/小程序
    TypeScripttry {
        const message = nim.V2NIMMessageCreator.createLocationMessage(30.25, 120.166664, "HangZhou")
    } catch(err) {
        // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
        const message = v2.messageCreator.createLocationMessage(latitude, longitude, address)
    } catch(err) {
        // todo error
    }
    
    HarmonyOS
    TypeScripttry {
        const message = nim.messageCreator.createLocationMessage(30.25, 120.166664, "HangZhou")
    } catch(err) {
        // todo error
    }
    
    Flutter
    Dartawait MessageCreator.createLocationMessage(latitude, longitude, address);
    
  3. 发送方 调用 sendMessage 方法,发送已构建的地理位置消息。

    示例代码请参考 实现文本消息收发

  4. 接收方 通过 onReceiveMessages 回调收到地理位置消息。

收发提示消息

提示消息(又称 Tip 消息)主要用于会话内的通知提醒。与自定义消息的区别在于,提示消息不支持设置附件。

提示消息的典型使用场景包括进入会话时出现的欢迎消息、会话过程中命中敏感词后的提示等。

API 调用时序

sequenceDiagram

par 步骤 1:初始化 SDK
发送方 ->> NIM: 初始化 SDK
接收方 ->> NIM: 初始化 SDK
end
par 步骤 2:发送方登录
发送方 ->> NIM: 登录
end
par 步骤 3:接收方注册消息监听并登录
接收方 ->> NIM: 监听消息接收<br>(addMessageListener)
接收方 ->> NIM: 登录
end
par 步骤 4:消息收发
发送方 ->> NIM: 构造提示消息<br>(createTipsMessage)
发送方 ->> NIM: 发送消息<br>(sendMessage)
NIM ->> 接收方: 投递消息体
end

实现步骤

  1. 接收方 注册消息监听器,监听消息接收回调事件。

    示例代码请参考 实现文本消息收发 的实现步骤 1。

  2. 发送方 调用 createTipsMessage 方法,构建一条提示消息。

    Android
    JavaV2NIMMessage v2TipMessage = V2NIMMessageCreator.createTipsMessage("tip content");
    
    iOS
    Objective-CV2NIMMessage *message = [V2NIMMessageCreator createTipsMessage:"tip text"];
    
    macOS/Windows
    C++auto tipMessage = V2NIMMessageCreator::createTipsMessage("text");
    if(!tipMessage) {
        // create tip message failed
    }
    
    Web/uni-app/小程序
    TypeScripttry {
        const newMessage = nim.V2NIMMessageCreator.createTipsMessage("hint")
    } catch(err) {
        // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
        const newMessage = v2.messageCreator.createTipsMessage("hint")
    } catch(err) {
        // todo error
    }
    
    HarmonyOS
    TypeScripttry {
        const newMessage = nim.messageCreator.createTipsMessage("hint")
    } catch(err) {
        // todo error
    }
    
    Flutter
    Dartawait MessageCreator.createTipsMessage(text);
    
  3. 发送方 调用 sendMessage 方法,发送已构建的提示消息。

    示例代码请参考 实现文本消息收发

  4. 接收方 通过 onReceiveMessages 回调收到提示消息。

收发自定义消息

除了上述内置消息类型外,NIM SDK 还支持自定义消息类型。

SDK 不负责定义和解析自定义消息,您需要自行完成。自定义消息存入消息数据库,会和内置消息一并展现在消息记录中。

实现步骤

  1. 调用 createCustomMessage 构建自定义消息。

    Android
    JavaV2NIMMessage v2CustomMessage = V2NIMMessageCreator.createCustomMessage(text, rawAttachment);
    
    iOS
    Objective-CV2NIMMessage *message = [V2NIMMessageCreator createCustomMessage:@"text"
                                                    rawAttachment:@"custoom JSON String"];
    
    macOS/Windows
    C++auto customMessage = V2NIMMessageCreator::createCustomMessage("text", R"({"key": "value"})");
    if(!customMessage) {
        // create custom message failed
    }
    
    Web/uni-app/小程序
    TypeScripttry {
        const message = nim.V2NIMMessageCreator.createCustomMessage("text", JSON.stringify({
            strategy: 1
        }))
    } catch(err) {
        // todo error
    }
    
    Node.js/Electron
    TypeScripttry {
        const message = v2.messageCreator.createCustomMessage(text, rawAttachment)
    } catch(err) {
        // todo error
    }
    
    HarmonyOS
    TypeScripttry {
        const message = nim.messageCreator.createCustomMessage("text", JSON.stringify({
            strategy: 1
        }))
    } catch(err) {
        // todo error
    }
    
    Flutter
    Dartawait MessageCreator.createCustomMessage(text, rawAttachment);
    
  2. 发送方 调用 sendMessage 方法,发送已构建的自定义消息。

    示例代码请参考 实现文本消息收发

  3. 接收方 通过 onReceiveMessages 回调收到自定义消息。

接收通知消息

通知消息区别于系统通知,属于 会话内消息,是网易云信服务器针对一些特定场景事件预置的消息,目前主要用于群和聊天室的事件通知。当事件发生时服务器下发通知消息到 SDK,您需要解析消息中附带的信息来获取通知内容。例如群通知消息,当有新成员进群时,群内成员将收到 新成员进群 的通知消息。

  1. 接收方 注册消息监听器,监听消息接收回调事件。

    示例代码请参考 实现文本消息收发 的实现步骤 1。

  2. 解析通知消息,具体请参考 群组通知消息

场景功能

发送消息后获取消息内容

调用 sendMessage 发送消息时,设置消息发送成功回调参数 success。如果消息发送成功,通过成功回调返回的结果接收消息对象。

发送消息后确认消息是否发送成功

发送消息时,设置消息发送成功回调参数 success 和消息发送失败参数 failure,监听消息发送是否成功。如果收到成功回调,则消息发送成功。

设置消息的扩展字段

会话内消息具有服务端扩展字段和客户端扩展字段。服务端扩展字段仅能在消息发送前设置,会多端同步。客户端扩展字段在消息发送前后均可设置,不会多端同步。

  • 扩展字段必须使用 JSON 格式封装,并传入非格式化的 JSON 字符串。
  • 最大长度为 1024 字节,可通过 网易云信控制台 IM 即时通讯下 基础功能 > 消息扩展字段上限调整 修改长度上限。
更新客户端扩展字段
  • 发送消息前设置:构造消息对象时,通过 localExtension 设置客户端扩展字段。
  • 发送消息后设置:调用 updateMessageLocalExtension 方法更新消息的本地扩展字段。
更新服务端扩展字段

构造消息对象时,通过 serverExtension 设置服务端端扩展字段。

插入本地消息

当有业务场景需要插入一条消息至本地数据库内而不发出时,可以通过 insertMessageToLocal 方法实现。例如本地提示类消息的 UI 展示,向群里插入一条 群创建成功 的提示消息。

该功能不支持 Web 端。

  1. 接收方 注册消息监听器,监听消息接收回调事件。

    示例代码请参考 实现文本消息收发 的实现步骤 1。

  2. 调用 createXXXMessage 方法,构建一条消息。

  3. 调用 insertMessageToLocal 方法,向本地数据库插入一条已构建的消息。插入消息成功后,本端会收到消息接收回调 onReceiveMessages 并通知 UI 界面更新。

    • 如果为单聊场景,可以插入一条发送或接收的消息。
    • 如果为群聊场景,可以插入一条任意群成员的消息。

    以文本消息为例:

    Android
    JavaV2NIMMessageService v2MessageService = NIMClient.getService(V2NIMMessageService.class);
    
    // 构建被插入的消息
    V2NIMMessage v2NIMMessage = V2NIMMessageCreator.createTextMessage("xxx");
    
    V2NIMConversationType conversationType = V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P;
    String conversationId = V2NIMConversationIdUtil.conversationId("xxx", conversationType);
    
    v2MessageService.insertMessageToLocal(v2NIMMessage, conversationId, "xxx", 0,
            new V2NIMSuccessCallback<NIMMessage>() {
                @Override
                public void onSuccess(NIMMessage nimMessage) {
    
                }
            },
            new V2NIMFailureCallback() {
                @Override
                public void onFailure(V2NIMError error) {
    
                }
            });
    
    iOS
    Objective-C[[[NIMSDK sharedSDK] v2MessageService] insertMessageToLocal:message
                                            conversationId:@"conversaionId"
                                                    senderId:@"senderId"
                                                createTime:1698809881
                                                    success:^(V2NIMMessage * _Nonnull result) {
        // result 插入消息成功
    } failure:^(V2NIMError * _Nonnull error) {
        // error 包含错误原因
    }];
    
    macOS/Windows
    C++auto conversationId = V2NIMConversationIdUtil::p2pConversationId("target_account_id");
    auto message = V2NIMMessageCreator::createTextMessage("hello world");
    messageService.insertMessageToLocal(
        message,
        conversationId,
        "target_account_id",
        0,
        [](V2NIMMessage message) {
            // Insert local message succeeded
        },
        [](V2NIMError error) {
            // Insert local message failed, handle error
    });
    
    Node.js/Electron
    TypeScriptconst message = await v2.messageService.insertMessageToLocal(message, conversationId, senderId, createTime)
    
    HarmonyOS
    TypeScriptconst messageResult:V2NIMMessage = await nim.messageService.insertMessageToLocal(message, conversationId)
    
    Flutter
    Dartawait NimCore.instance.messageService.insertMessageToLocal(message, conversationId, senderId, createTime);
    

重发消息

如因网络等原因消息发送失败,您可以再次调用 sendMessage 方法重发消息。SDK 会判断本地或缓存中是否有该消息,如有则视为消息重发。

涉及接口

Android/iOS/macOS/Windows
API 说明
addMessageListener 注册消息相关监听器
removeMessageListener 取消注册消息相关监听器
createXXXMessage 消息构建,包括创建一条文本/图片/语音/视频/文件/地理/提示/自定义消息
sendMessage 发送消息
addCustomStorageScene 添加自定义存储场景
downloadFile 下载文件
cancelMessageAttachmentUpload 取消上传消息附件
updateMessageLocalExtension 更新消息的本地扩展字段
insertMessageToLocal 插入一条消息到本地数据库
V2NIMStorageSceneConfig 网易对象存储 NOS 文件存储场景配置
V2NIMMessageAttachment 消息附件对象
Web/uni-app/小程序/Node.js/Electron/HarmonyOS
API 说明
on("EventName") 注册消息相关监听器
off("EventName") 取消注册消息相关监听器
createXXXMessage 消息构建,包括创建一条文本/图片/语音/视频/文件/地理/提示/自定义消息
sendMessage 发送消息
addCustomStorageScene 添加自定义存储场景
downloadFile 下载文件
cancelMessageAttachmentUpload 取消上传消息附件
updateMessageLocalExtension 更新消息的本地扩展字段
insertMessageToLocal 插入一条消息到本地数据库
V2NIMStorageSceneConfig 网易对象存储 NOS 文件存储场景配置
V2NIMMessageAttachment 消息附件对象
Flutter
API 说明
add 注册消息相关监听器
cancel 取消注册消息相关监听器
createXXXMessage 消息构建,包括创建一条文本/图片/语音/视频/文件/地理/提示/自定义消息
sendMessage 发送消息
addCustomStorageScene 添加自定义存储场景
downloadFile 下载文件
cancelMessageAttachmentUpload 取消上传消息附件
updateMessageLocalExtension 更新消息的本地扩展字段
insertMessageToLocal 插入一条消息到本地数据库
NIMStorageSceneConfig 网易对象存储 NOS 文件存储场景配置
NIMMessageAttachment 消息附件对象
此文档是否对你有帮助?
有帮助
去反馈
  • 支持平台
  • 技术原理
  • 前提条件
  • 频控限制
  • 收发文本消息
  • 收发富媒体消息
  • 收发地理位置消息
  • 收发提示消息
  • 收发自定义消息
  • 接收通知消息
  • 场景功能
  • 发送消息后获取消息内容
  • 发送消息后确认消息是否发送成功
  • 设置消息的扩展字段
  • 插入本地消息
  • 重发消息
  • 涉及接口