消息收发
更新时间: 2024/03/26 15:24:31
NetEase IM Android SDK(以下简称“NIM SDK”)支持收发多种消息类型,助您快速实现多样化的消息业务场景。NIM SDK 提供MessageBuilder
类、MsgServiceObserve
接口和MsgService
接口,支持构建、监听和收发多种类型的消息。SDK 中定义消息的结构为IMMessage
(不支持继承扩展),不同消息类型以MsgTypeEnum
作区分。发送不同类型消息的方法均为sendMessage
。单聊消息和群聊消息都通过该方法发送,通过参数sessionType
设置发送的为单聊消息还是群聊消息。
本文介绍通过网易云信 NIM SDK 实现消息收发的技术原理、前提条件以及具体的实现流程。
- 超大群、聊天室和圈组的消息收发,需分别使用
SuperTeamService
、ChatRoomService
和QChatMessageService
的消息发送接口,与单聊和群聊的消息发送接口不同。具体实现流程请分别参见超大群消息收发、聊天室消息收发和圈组消息收发。 - 本文的时序图可能因为网络问题而显示异常。如显示异常,一般刷新当前页面即可正常显示。
技术原理
应用集成 NIM SDK 并完成 SDK 初始化后,消息收发流程如下图所示(本地提示消息和通知消息除外)。
上图中的流程可归纳为如下三步:
- 账号集成与登录。
- 开发者将应用的用户账号传入云信 IM 服务器(又称 accid)。
- 云信 IM 服务器返回 Token 给应用服务器。
- 应用客户端登录应用服务器。
- 应用服务器将 Token 返回给应用客户端。
- 用户带 Token 登录云信 IM 服务器。
- 用户A 发送一条消息到云信 IM 服务器。
- 云信 IM 服务器投递消息至其他用户,分为如下两种情况:
- 如为单聊消息,IM 服务器将其投递至用户B。
- 如为群聊消息,IM 服务器将其投递至群内其他每一位用户。
上图仅以静态 Token 登录为例展示消息收发流程。网易云信 IM 还支持动态 Token 登录鉴权和第三方回调登录鉴权,相关详情请参见登录鉴权。
前提条件
在实现消息收发之前,请确保:
API 使用限制
发送消息(sendMessage
)的方法调用存在频控,一分钟内默认最多可调用 300 次。
实现消息收发
收发文本消息
实现流程
-
发送方和接收方调用
observeMsgStatus
方法注册消息状态变化观察者,监听消息状态MsgStatusEnum
的变化。// 监听消息状态变化 NIMClient.getService(MsgServiceObserve.class).observeMsgStatus(statusObserver, register); private Observer<IMMessage> statusObserver = new Observer<IMMessage>() { @Override public void onEvent(IMMessage msg) { // 1、根据sessionId判断是否是自己的消息 // 2、更改内存中消息的状态 // 3、刷新界面 } };
-
接收方调用
observeReceiveMessage
方法,注册消息接收观察者,监听消息到达事件。Observer<List<IMMessage>> incomingMessageObserver = new Observer<List<IMMessage>>() { @Override public void onEvent(List<IMMessage> messages) { // 处理新收到的消息,为了上传处理方便,SDK 保证参数 messages 全部来自同一个聊天对象。 } } NIMClient.getService(MsgServiceObserve.class) .observeReceiveMessage(incomingMessageObserver, true);
-
发送方调用
createTextMessage
方法,构建一条文本消息。通过
sessionType
参数,可设置发送的文本消息为单聊消息或群聊消息。如设置为群聊消息,请确保已创建相应的群组。参数 说明 sessionId
聊天对象的 ID,根据会话类型 sessionType
判断- 如果是单聊,则
sessionId
为用户的云信IM帐号(即accid
) - 如果是群聊,则
sessionId
为群组 ID
sessionType
会话类型, SessionTypeEnum.P2P
为单聊类型,SessionTypeEnum.Team
为群聊类型text
文本消息内容 - 如果是单聊,则
-
发送方调用
sendMessage
方法,发送已构建的文本消息。可通过消息配置选项
CustomMessageConfig
设置该消息是否存入云端、写入漫游、计入未读数等。具体配置示例请参见消息配置选项。// 该帐号为示例 String account = "testAccount"; // 以单聊类型为例 SessionTypeEnum sessionType = SessionTypeEnum.P2P; String text = "this is an example"; // 创建一个文本消息 IMMessage textMessage = MessageBuilder.createTextMessage(account, sessionType, text); // 发送给对方 NIMClient.getService(MsgService.class).sendMessage(textMessage, false).setCallback(new RequestCallback<Void>() { @Override public void onSuccess(Void param) { } @Override public void onFailed(int code) { } @Override public void onException(Throwable exception) { } });
-
接收方通过
observeReceiveMessage
触发的回调函数收到文本消息。
收发多媒体消息
多媒体消息包括图片消息、语音消息、视频消息和文件消息。
NIM SDK 提供了高清语音的录制与播放的功能,用于处理语音消息。相关详情请参见语音消息处理。
API调用时序
实现流程
-
发送方和接收方调用
observeMsgStatus
方法注册消息状态变化观察者,监听消息状态MsgStatusEnum
和消息附件接收或发送状态AttachStatusEnum
的变化。// 监听消息状态变化 NIMClient.getService(MsgServiceObserve.class).observeMsgStatus(statusObserver, register); private Observer<IMMessage> statusObserver = new Observer<IMMessage>() { @Override public void onEvent(IMMessage msg) { // 1、根据sessionId判断是否是自己的消息 // 2、更改内存中消息的状态 // 3、刷新界面 } };
-
发送方和接收方调用
observeAttachmentProgress
注册消息附件上传/下载观察者,监听消息附件的上传/下载进度。 -
接收方调用
observeReceiveMessage
方法,注册消息接收观察者,监听消息接收。Observer<List<IMMessage>> incomingMessageObserver = new Observer<List<IMMessage>>() { @Override public void onEvent(List<IMMessage> messages) { // 处理新收到的消息,为了上传处理方便,SDK 保证参数 messages 全部来自同一个聊天对象。 } } NIMClient.getService(MsgServiceObserve.class) .observeReceiveMessage(incomingMessageObserver, true);
-
发送方构建多媒体消息。
消息类型创建方法 图片消息 调用 createImageMessage
方法,构建一条图片消息。也可调用其重载方法,指定图片上传时使用的多媒体资源存储场景。语音消息 调用 createAudioMessage
方法,构建一条语音消息。也可调用其重载方法,指定音频上传时使用的多媒体资源存储场景。视频消息 调用 createVideoMessage
方法,构建一条视频消息。也可调用其重载方法,指定视频上传时使用的多媒体资源存储场景。文件消息 调用 createFileMessage
方法,构建一条文件消息。也可调用其重载方法,指定文件上传时使用的多媒体资源存储场景。上述方法的部分参数说明如下:
参数 说明 sessionId
聊天对象的 ID,根据会话类型 sessionType
判断- 如果是单聊,则
sessionId
为用户的云信IM帐号(即accid
) - 如果是群聊,则
sessionId
为群组 ID
sessionType
会话类型, SessionTypeEnum.P2P
为单聊类型,SessionTypeEnum.Team
为群聊类型 - 如果是单聊,则
-
发送方调用
sendMessage
方法,发送已构建的多媒体消息。- 可通过消息配置选项
CustomMessageConfig
设置该消息是否存入云端、写入漫游、计入未读数等。具体配置示例请参见消息配置选项。 - 如发送的为图片消息、视频消息或文件消息,发送后可调用
cancelUploadAttachment
方法取消附件(图片、视频或文件)的上传。如果附件已经上传成功,操作将会失败 。如果成功取消了附件的上传,那么相应的消息会发送失败,对应的消息状态是MsgStatusEnum.fail
,附件状态是AttachStatusEnum.cancel
。 - 如果同一个图片/文件消息需要多次发送时,建议控制时序依次发送,避免出现发送失败的问题。
构建并发送多媒体消息的示例代码如下:
发送图片消息// 该帐号为示例,请先注册 String account = "testAccount"; // 以单聊类型为例 SessionTypeEnum sessionType = SessionTypeEnum.P2P; // 示例图片,需要开发者在相应目录下有图片 File file = new File("/sdcard/test.jpg"); // 创建一个图片消息 IMMessage message = MessageBuilder.createImageMessage(account, sessionType, file, file.getName()); // 或者:创建一个图片消息并指定图片上传时使用的文件资源场景,"nos_scene_key"请替换成开发者已经配置的 IMMessage message = MessageBuilder.createImageMessage(account, sessionType, file, file.getName(),"nos_scene_key"); // 发送给对方 NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() { @Override public void onSuccess(Void param) { } @Override public void onFailed(int code) { } @Override public void onException(Throwable exception) { } });
发送语音消息// 该帐号为示例,请先注册 String account = "testAccount"; // 以单聊类型为例 SessionTypeEnum sessionType = SessionTypeEnum.P2P; // 示例音频,需要开发者在相应目录下有文件 File audioFile = new File("/sdcard/testAudio.mp3"); // 音频时长,时间为示例 long audiolength = 2000; // 创建音频消息 IMMessage audioMessage = MessageBuilder.createAudioMessage(account, sessionType, audioFile, audioLength); // 或者:创建一个音频消息并指定音频上传时使用的文件资源场景,"nos_scene_key"请替换成开发者已经配置的 IMMessage audioMessage = MessageBuilder.createAudioMessage(account, sessionType, audioFile, "nos_scene_key"); // 发送给对方 NIMClient.getService(MsgService.class).sendMessage(audioMessage, false).setCallback(new RequestCallback<Void>() { @Override public void onSuccess(Void param) { } @Override public void onFailed(int code) { } @Override public void onException(Throwable exception) { } });
发送视频消息// 该帐号为示例,请先注册 String account = "testAccount"; // 以单聊类型为例 SessionTypeEnum sessionType = SessionTypeEnum.P2P; // 示例视频,需要开发者在相应目录下有文件 File file = new File("/sdcard/testVideo.mp4"); // 获取视频mediaPlayer MediaPlayer mediaPlayer; try { mediaPlayer = MediaPlayer.create(context, Uri.fromFile(file)); } catch (Exception e) { e.printStackTrace(); } // 视频文件持续时间 long duration = mediaPlayer == null ? 0 : mediaPlayer.getDuration(); // 视频高度 int height = mediaPlayer == null ? 0 : mediaPlayer.getVideoHeight(); // 视频宽度 int width = mediaPlayer == null ? 0 : mediaPlayer.getVideoWidth(); // 创建视频消息 IMMessage message = MessageBuilder.createVideoMessage(account, sessionType, file, duration, width, height, null); // 或者:创建一个视频消息并指定视频上传时使用的文件资源场景,"nos_scene_key"请替换成开发者已经配置的 IMMessage message = MessageBuilder.createVideoMessage(account, sessionType, file, duration, width, height, null,"nos_scene_key"); // 发送给对方 NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() { @Override public void onSuccess(Void param) { } @Override public void onFailed(int code) { } @Override public void onException(Throwable exception) { } });
发送文件消息// 该帐号为示例,请先注册 String account = "testAccount"; // 以单聊类型为例 SessionTypeEnum sessionType = SessionTypeEnum.P2P; // 示例文件,需要开发者在相应目录下有文件 File file = new File("/sdcard/test.txt"); // 创建文件消息 IMMessage message = MessageBuilder.createFileMessage(account, sessionType, file, file.getName()); // 或者:创建一个文件消息并指定文件上传时使用的文件资源场景,"nos_scene_key"请替换成开发者已经配置的 IMMessage message = MessageBuilder.createFileMessage(account, sessionType, file, file.getName(),"nos_scene_key"); // 发送给对方 NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() { @Override public void onSuccess(Void param) { } @Override public void onFailed(int code) { } @Override public void onException(Throwable exception) { } });
- 可通过消息配置选项
-
接收方通过
observeReceiveMessage
的回调函数接收多媒体消息。多媒体资源一般默认自动下载,具体的默认下载策略请参见下表:
消息类型 默认资源下载策略 图片/视频消息 SDK 在收到消息时,自动下载缩略图和封面图片 语音消息 SDK 在收到消息时,自动下载原音频 文件消息 SDK 默认不下载原文件 - 如果需要下载文件消息的文件资源,可调用
downloadAttachment
方法手动下载。其他类型多媒体消息的资源如自动下载失败,也可调用该方法手动重新下载。 - 如需自主选择下载时机,需将初始化配置参数
SDKOptions - preloadAttach
设置为false
,关闭默认资源下载策略,再在合适的时机调用downloadAttachment
方法。
手动下载多媒体资源示例如下:
// 下载之前判断一下是否已经下载。若重复下载,会报错误码414。(以SnapChatAttachment为例) private boolean isOriginImageHasDownloaded(final IMMessage message) { if (message.getAttachStatus() == AttachStatusEnum.transferred && !TextUtils.isEmpty(((SnapChatAttachment) message.getAttachment()).getPath())) { return true; } return false; } // 因为下载的文件可能会很大,这个接口返回类型为 AbortableFuture ,允许用户中途取消下载。 AbortableFuture future = NIMClient.getService(MsgService.class).downloadAttachment(message, false);
- 如果需要下载文件消息的文件资源,可调用
-
多媒体资源下载完后,接收方可通过对应的
MsgAttachment
获取到具体的附件内容。多媒体附件的基类是FileAttachment
,继承自MsgAttachment
。
收发地理位置消息
地理位置消息收发流程与文本消息收发流程基本一致,区别在于构建消息的调用方法不同(需调用createLocationMessage
)。本节仅简要展示相关调用示例,具体实现流程请参考收发文本消息。
可通过消息配置选项CustomMessageConfig
设置该消息是否存入云端、写入漫游、计入未读数等。具体配置示例请参见消息配置选项。
API调用示例
调用createLocationMessage
构建地理位置消息后,调用sendMessage
方法将其发送至接收方的示例代码如下:
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 纬度
double lat = 30.3;
// 经度
double lng = 120.2;
// 地理位置描述信息
String addr = "杭州";
// 创建地理位置信息
IMMessage message = MessageBuilder.createLocationMessage(account, sessionType, lat, lng, addr);
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() {
@Override
public void onSuccess(Void param) {
}
@Override
public void onFailed(int code) {
}
@Override
public void onException(Throwable exception) {
}
});
收发提示消息
提示消息(又叫做 Tip 消息)主要用于会话内的通知提醒,可以看做是自定义消息的简化,有独立的消息类型MsgTypeEnum.tip
。 区别于自定义消息,Tip 消息暂不支持 setAttachment
设置附件,如需使用附件请使用自定义消息。 Tip 消息的典型使用场景包括进入会话时出现的欢迎消息和会话过程中命中敏感词后的提示等。当然这些应用场景也可以用自定义消息实现,但会相对复杂。
本节以 “您撤回了一条消息提示出现在群组中(仅对发送者可见且不发送到服务端)” 这个应用场景为例,介绍实现提示消息收发的流程。
API调用时序
实现流程
本节仅对上述标为橙色的流程作补充说明。撤回消息相关详情,请参见消息撤回。
-
用户A 在登录 IM 前,调用
observeMsgStatus
方法注册消息状态变化观察者,监听消息状态MsgStatusEnum
的变化。// 监听消息状态变化 NIMClient.getService(MsgServiceObserve.class).observeMsgStatus(statusObserver, register); private Observer<IMMessage> statusObserver = new Observer<IMMessage>() { @Override public void onEvent(IMMessage msg) { // 1、根据sessionId判断是否是自己的消息 // 2、更改内存中消息的状态 // 3、刷新界面 } };
-
用户A 调用
observeReceiveSystemMsg
方法,注册系统消息接收事件观察者,监听系统通知(本场景下为监听消息撤回系统通知)。NIMClient.getService(SystemMessageObserver.class) .observeReceiveSystemMsg(new Observer<SystemMessage>() { @Override public void onEvent(SystemMessage message) { // 收到系统通知,可以做相应操作 } }, register);
-
用户A 调用
createTeam
方法创建高级群,相关示例代码请参见创建群组。 -
其他用户加入用户A 创建的高级群,具体加入方法请参见加入群组。
-
用户A 调用
createTipMessage
方法构建提示消息。该方法的部分参数说明如下:
参数 说明 sessionId
聊天对象的 ID,根据会话类型 sessionType
判断- 如果是单聊,则
sessionId
为用户的云信IM帐号(即accid
) - 如果是群聊,则
sessionId
为群组 ID
sessionType
会话类型, SessionTypeEnum.P2P
为单聊类型,SessionTypeEnum.Team
为群聊类型 - 如果是单聊,则
-
用户A 调用
setConfig
方法,调用时将enableUnreadCount
设置为false
,使该提示消息不计入未读计数。 -
用户A 调用
saveMessageToLocalEx
方法,保存该提示消息到本地数据库,但不发送到服务器。第 5 至 7 步的示例代码如下:
java
IMMessage message = MessageBuilder.createTipMessage(sessionId, sessionType); message.setContent("您撤回了一条消息"); message.setStatus(MsgStatusEnum.success); CustomMessageConfig config = new CustomMessageConfig(); config.enableUnreadCount = false; message.setConfig(config); NIMClient.getService(MsgService.class).saveMessageToLocalEx(message, true, time);
接收通知消息
针对一些特定场景的事件,云信服务器预置了一些通知消息,在事件发生时下发到 SDK。通知消息也是一种特定消息,开发者需解析消息中附带的信息,来获取通知内容。如最常见的通知消息——群通知事件,如有新成员进群,则群内已有成员将收到此通知消息。
-
通知消息属于会话内的一种消息,其对应的数据结构为
IMMessage
,消息类型为MsgTypeEnum#notification
。通知消息目前用于在群和聊天室的事件通知。 -
通知消息需要进行解析,具体请参见群组通知消息。
-
可对通知消息进行过滤,具体请参见通知消息过滤。
收发自定义消息
除了上述内置消息类型以外,NIM Android SDK 还支持收发自定义消息类型。具体实现流程介绍,请参见自定义消息收发。
常见问题
发送消息后如何获取消息内容
通过 observeMsgStatus
注册消息状态变化观察者,该方法可以监听消息发送状态变化中回调IMMessage
对象。
可以通过IMMessage
对象的如下方法获取消息内容:
getDirect
方法获取消息方向getSessionId
方法获取聊天对象的accid/群组idgetContent
方法获取文本消息具体内容getAttachment
方法获取文件消息附件对象getStatus
方法获取消息收发状态getTime
方法获取消息时间(单位为毫秒)
如何判断消息已发送成功
调用消息发送接口时,设置回调函数:NIMClient.getService(MsgService.class).sendMessage(IMMessage msg, boolean resend).setCallback(callback)
,判断是否进入onSuccess
回调。
如何设置消息的扩展字段
单聊或群聊消息具有服务端扩展字段和客户端扩展字段。聊天室消息没有客户端扩展字段。
服务端扩展字段只能在消息发送前设置,会同步到其他端;客户端扩展字段在消息发送前后设置均可,不会同步到其他端。
扩展字段,请使用 JSON 格式封装,并传入非格式化的 JSON 字符串,最大长度 1024 字节。
具体方法如下:
-
对于单聊或群聊消息,构造
IMMessage
对象时,通过setLocalExtension
方法设置客户端扩展字段。 -
调用
updateIMMessage
方法更新消息的本地扩展字段。设置消息的客户端扩展字段后,必须调用
updateIMMessage
方法,否则无法生效。
对于单聊或群聊消息,构造IMMessage
对象时,通过setRemoteExtension
方法设置消息的服务端扩展字段。