消息收发

消息功能概述

SDK 提供一套完善的消息传输管理服务,包括收发消息,存储消息,上传下载附件,管理最近联系人等。

SDK 原生支持发送文本,语音,图片,视频,文件,地理位置,提醒,智能对话机器人等 8 种类型消息,同时支持用户发送自定义的消息类型。

消息功能具体介绍可参考产品介绍的 基础消息功能

网易云通信消息对象均为 IMMessage,不同消息类型以 MsgTypeEnum 作区分。IMMessage 不支持继承扩展,请使用组合的方式。

返回值 IMMessage接口 说明
String getUuid() 获取消息的uuid, 该域在生成消息时即会填上
String getSessionId() 获取聊天对象的Id(好友帐号,群ID等)
SessionTypeEnum getSessionType() 获取会话类型,
SessionTypeEnum.P2P 为单聊类型,
SessionTypeEnum.Team 为群聊类型,
SessionTypeEnum.System 为系统消息,
SessionTypeEnum.ChatRoom 为聊天室消息
String getFromNick() 获取消息发送者的昵称
MsgTypeEnum getMsgType() 获取消息类型。MsgTypeEnum的类型,请见下表
MsgStatusEnum getStatus() 获取消息接收/发送状态
MsgDirectionEnum getDirect() 获取消息方向,
MsgDirectionEnum.Out 表示发出去的消息,
MsgDirectionEnum.In 表示接收到的消息
String getContent() 获取消息具体内容,
当消息类型 为MsgTypeEnum.text 时,该域为消息内容。
当为其他消息类型时,该域为可选项,
如果设置,将作为 iOS 的 apns 推送文本以及 android 内置消息推送的显示文本
( 1.7.0及以上版本建议使用 pushContent )
long getTime() 获取消息时间,单位为 ms
String getFromAccount() 获取该条消息发送方的帐号
void setAttachment
(MsgAttachment
attachment)
设置消息附件对象。详细见 MsgAttachment 参数说明
MsgAttachment getAttachment() 获取消息附件对象。仅当 getMsgType() 返回为非 text 时有效
AttachStatusEnum getAttachStatus() 获取消息附件接收/发送状态,
AttachStatusEnum.def 表示默认状态,未开始。
AttachStatusEnum.transferring 表示正在传输。
AttachStatusEnum.transferred 表示传输成功。
AttachStatusEnum.fail 表示传输失败
CustomMessageConfig getConfig() 获取消息配置。具体见CustomMessageConfig参数说明
Map getRemoteExtension() 获取扩展字段(该字段会发送到其他端)
void setRemoteExtension
(Map
remoteExtension)
设置扩展字段
(该字段会发送到其他端),
最大长度 1024 字节。
开发者需要保证此 Map 能够转换为 JsonObject
Map getLocalExtension() 获取本地扩展字段(仅本地有效)
void setLocalExtension
(Map
localExtension)
设置本地扩展字段
(该字段仅在本地使用有效,不会发送给其他端),
最大长度 1024 字节。
开发者需要保证此 Map 能够转换为 JsonObject
String getPushContent() 获取自定义推送文案
void setPushContent(String pushContent) 设置自定义推送文案
( 1.7.0 及以上版本建议使用此字段,不要使用 setContent 来设置推送文案),
最大长度 200 字节
Map getPushPayload() 获取第三方自定义的推送属性
void setPushPayload
(Map
pushPayload)
设置第三方自定义的推送属性,
开发者需要保证此 Map 能够转换为 JsonObject,
最大长度 2048 字节
MemberPushOption getMemberPushOption() 获取指定成员推送选项,详细介绍见指定推送设置详解章节
boolean isRemoteRead() 判断自己发送的消息对方是否已读,
true:对方已读;false:对方未读
int getFromClientType() 获取消息发送方类型,与 ClientType 类比较
NIMAntiSpamOption getNIMAntiSpamOption() 获取反垃圾配置项,详细介绍见反垃圾设置详解章节
MsgTypeEnum参数 说明
undef 未知消息
text 文本消息
image 图片消息
audio 音频消息
video 视频消息
location 位置消息
file 文件消息
avchat 音视频通话消息
notification 通知消息
tip 提醒类型消息
robot 机器人消息
custom 第三方 App 自定义消息

消息内容根据类型不同也不一样。文本消息最为简单,消息内容就是 content 字符串。其他消息类型均带有一个消息附件对象 MsgAttachment,该对象在传输时一般序列化为 json 格式字符串;此外,所有实现 MsgAttachment 接口的成员都必须实现 Serializable 接口。内建的消息附件主要有以下几种:

内建消息附件 说明
LocationAttachment 位置消息附件对象类型。
FileAttachment 文件消息附件对象类型(继承该附件, SDK 在发送消息时将自动上传文件)。
ImageAttachment 图片消息附件对象类型。
AudioAttachment 音频消息附件对象类型。
VideoAttachment 视频消息附件对象类型。
RobotAttachment 机器人消息附件对象类型。
NotificationAttachment 通知类消息附件基类,目前主要用于群类各种操作事件的通知。
该类型事件仅由服务器生成和发送,客户端不会生成该类型的消息。

消息发送

发送原生支持的消息类型流程比较简单,先通过 MessageBuilder 提供的接口创建消息对象,然后调用 MsgServicesendMessage 接口发送出去即可。

/**
 * 发送消息。
 * @param msg    带发送的消息体,由{@link MessageBuilder}构造
 * @param resend 如果是发送失败后重发,标记为true,否则填false
 * @return InvocationFuture 可以设置回调函数。消息发送完成后才会调用,如果出错,会有具体的错误代码。
 */
public InvocationFuture<Void> sendMessage(IMMessage msg, boolean resend);

文本消息

首先创建文本消息,调用 sendMessage 方法发送。

文本消息创建原型:

/**
 * 创建一条普通文本消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param text        文本消息内容
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createTextMessage(String sessionId, SessionTypeEnum sessionType, String text);
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
text 文本消息内容
// 该帐号为示例,请先注册
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);

图片消息

图片消息创建原型

/**
 * 创建一条图片消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param file        图片文件
 * @param displayName 图片文件的显示名,可不同于文件名
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createImageMessage(String sessionId, SessionTypeEnum sessionType, File file, String displayName);

// 或者:创建一条图片消息并指定图片上传时使用的NOS场景
public static IMMessage createImageMessage(String sessionId, SessionTypeEnum sessionType, File file, String displayName, String nosTokenSceneKey)
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
file 图片文件对象
displayName 图片文件的显示名,可不同于文件名,可设置为 null
nosTokenSceneKey 图片上传时使用的 nos scene ,默认为NimNosSceneKeyConstant.NIM_DEFAULT_IM ,参考 NOS上传场景
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 示例图片,需要开发者在相应目录下有图片
File file = new File("/sdcard/test.jpg");
// 创建一个图片消息
IMMessage message = MessageBuilder.createImageMessage(account, sessionType, file, file.getName());
// 或者:创建一个图片消息并指定图片上传时使用的NOS场景,"nos_scene_key"请替换成开发者已经配置的
IMMessage message = MessageBuilder.createImageMessage(account, sessionType, file, file.getName(),"nos_scene_key");
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false);

音频消息

音频消息创建原型:

/**
 * 创建一条音频消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param file        音频文件对象
 * @param duration    音频文件持续时间,单位是ms
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createAudioMessage(String sessionId, SessionTypeEnum sessionType, File file, long duration);

//或者:创建一条音频消息并指定音频上传时使用的NOS场景
public static IMMessage createAudioMessage(String sessionId, SessionTypeEnum sessionType, File file, long duration, String nosTokenSceneKey)
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
file 音频文件对象
duration 音频文件持续时间,单位是 ms
nosTokenSceneKey 音频文件上传时使用的 nos scene ,默认为NimNosSceneKeyConstant.NIM_DEFAULT_IM ,参考 NOS上传场景
// 该帐号为示例,请先注册
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场景,"nos_scene_key"请替换成开发者已经配置的
IMMessage audioMessage = MessageBuilder.createAudioMessage(account, sessionType, audioFile, "nos_scene_key");
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(audioMessage, false);

视频消息

视频消息创建原型:

/**
 * 创建一条视频消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param file        视频文件对象
 * @param duration    视频文件持续时间
 * @param width       视频宽度
 * @param height      视频高度
 * @param displayName 视频文件显示名,可以为空
 * @return 视频消息
 */
public static IMMessage createVideoMessage(String sessionId, SessionTypeEnum sessionType, File file, long duration, int width, int height, String displayName);

//或者:创建一条视频消息并指定视频上传时使用的NOS场景
public static IMMessage createVideoMessage(String sessionId, SessionTypeEnum sessionType, File file, long duration, int width, int height, String displayName, String nosTokenSceneKey)
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
file 视频文件对象
duration 视频文件持续时间,单位 ms
width 视频宽度
height 视频高度
displayName 视频文件显示名,可为 null
nosTokenSceneKey 视频文件上传时使用的 nos scene ,默认为NimNosSceneKeyConstant.NIM_DEFAULT_IM ,参考 NOS上传场景
// 该帐号为示例,请先注册
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场景,"nos_scene_key"请替换成开发者已经配置的
IMMessage message = MessageBuilder.createVideoMessage(account, sessionType, file, duration, width, height, null,"nos_scene_key");
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false);

文件消息

文件消息创建原型:

/**
 * 创建一条文件消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param file        文件
 * @param displayName 文件的显示名,可不同于文件名
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createFileMessage(String sessionId, SessionTypeEnum sessionType, File file, String displayName);

//或者:创建一条文件消息并指定文件上传时使用的NOS场景
public static IMMessage  createFileMessage(String sessionId, SessionTypeEnum sessionType, File file, String displayName, String nosTokenSceneKey)
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
file 文件
displayName 文件的显示名,可不同于文件名
nosTokenSceneKey 文件上传时使用的 nos scene ,默认为NimNosSceneKeyConstant.NIM_DEFAULT_IM ,参考 NOS上传场景
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 示例文件,需要开发者在相应目录下有文件
File file = new File("/sdcard/test.txt");
// 创建文件消息
IMMessage message = MessageBuilder.createFileMessage(account, sessionType, file, file.getName());
// 或者:创建一个文件消息并指定文件上传时使用的NOS场景,"nos_scene_key"请替换成开发者已经配置的
IMMessage message = MessageBuilder.createFileMessage(account, sessionType, file, file.getName(),"nos_scene_key");
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false);

地理位置消息

地理位置消息创建原型:

/**
 * 创建一条地理位置信息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param lat         纬度
 * @param lng         经度
 * @param addr        地理位置描述信息
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createLocationMessage(String sessionId, SessionTypeEnum sessionType, double lat, double lng, String addr);
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
lat 纬度
lng 经度
addr 地理位置描述信息
// 该帐号为示例,请先注册
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);

机器人提问消息

机器人提问消息是指,普通用户发送给机器人的消息(也称作机器人上行消息)。机器人提问消息创建原型:

/**
 * @param sessionId    聊天对象ID
 * @param sessionType  会话类型
 * @param robotAccount @机器人账号
 * @param text         消息显示的文案,一般基于content加上@机器人的标签作为消息显示的文案。
 * @param type         机器人消息类型,参考{@link com.netease.nimlib.sdk.robot.model.RobotMsgType}
 * @param content      消息内容,如果消息类型是{@link com.netease.nimlib.sdk.robot.model.RobotMsgType#TEXT},必须传入说话内容
 * @param target       如果消息类型是{@link com.netease.nimlib.sdk.robot.model.RobotMsgType#LINK}, 必须传入跳转目标
 * @param params       如果消息类型是{@link com.netease.nimlib.sdk.robot.model.RobotMsgType#LINK}时,可能需要传入参数
 * @return 机器人消息
 */
public static IMMessage createRobotMessage(String sessionId, SessionTypeEnum sessionType, String robotAccount, String text, String type, String content, String target, String params);
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
robotAccount 机器人帐号
text 消息显示的文案,一般基于 content 加上机器人的标签作为消息显示的文案。
type 机器人消息类型 RobotMsgType。
RobotMsgType.WELCOME 表示欢迎消息,
RobotMsgType.TEXT 表示文本消息,
RobotMsgType.LINK 表示模块跳转消息
content 消息内容,如果机器人消息类型是 RobotMsgType.TEXT,必须传入说话内容
target 跳转目标, 如果机器人消息类型是 RobotMsgType.LINK,必须传入跳转目标
params 跳转参数,如果消息类型是 RobotMsgType.LINK时,可能需要传入参数
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 机器人帐号
String robotAccount = "robotAccount";
// 消息文案
String text = "这是消息文案";
// 机器人消息类型, 文本消息类型
String type = RobotMsgType.TEXT;
// 消息内容
String content = "这是消息内容";
// 创建机器人消息
IMMessage message = MessageBuilder.createRobotMessage(account, sessionType, robotAccount, text, type, content, null, null);

// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false);

机器人跳转消息

机器人跳转消息的原型和参数说明,参考机器人提问消息章节。机器人消息类型变为 RobotMsgType.LINK,同时注意,必须传入跳转目标。

// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 机器人帐号
String robotAccount = "robotAccount";
// 消息文案
String text = "这是消息文案";
// 机器人消息类型, 跳转消息类型
String type = RobotMsgType.LINK;
// 消息内容
String content = "这是消息内容";
// view是界面元素
RobotLinkView robotLinkView = (RobotLinkView) view;
LinkElement element = robotLinkView.getElement();
// 跳转目标
String target;
// 跳转参数
String params;
if (element != null) {
    target = element.getTarget();
    params = element.getParams();
}

// 创建机器人消息
IMMessage message = MessageBuilder.createRobotMessage(account, sessionType, robotAccount, text, type, content, target, params);

// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false);

通知消息

通知消息属于会话中的一种消息,其对应的数据结构为 IMMessage,消息类型为 MsgTypeEnum.notification。目前用于(已操作完成的)群通知和聊天室通知事件。通知消息由服务器下发,客户端无法生成通知消息。

通知消息的附件类型基类为 NotificationAttachment,通过 NotificationAttachment#getType() 可以得到通知消息的类型 NotificationType,可根据 NotificationType 区分不同的通知消息,具体类型如下:

NotificationType属性 说明
undefined 未定义类型
InviteMember TEAM:邀请群成员,用于讨论组中,讨论组可直接拉人入群
KickMember TEAM:移除群成员
LeaveTeam TEAM:有成员离开群
UpdateTeam TEAM:群资料更新
DismissTeam TEAM:群被解散
PassTeamApply TEAM:管理员通过用户入群申请
TransferOwner TEAM:群组拥有者权限转移通知
AddTeamManager TEAM:新增管理员通知
RemoveTeamManager TEAM:撤销管理员通知
AcceptInvite TEAM:用户接受入群邀请
MuteTeamMember TEAM:群成员禁言/解禁
ChatRoomMemberIn ChatRoom:成员进入聊天室
ChatRoomMemberExit ChatRoom:成员离开聊天室
ChatRoomMemberBlackAdd ChatRoom:成员被加黑
ChatRoomMemberBlackRemove ChatRoom:成员被取消黑名单
ChatRoomMemberMuteAdd ChatRoom:成员被设置禁言
ChatRoomMemberMuteRemove ChatRoom:成员被取消禁言
ChatRoomManagerAdd ChatRoom:设置为管理员
ChatRoomManagerRemove ChatRoom:取消管理员
ChatRoomCommonAdd ChatRoom:成员设定为固定成员
ChatRoomCommonRemove ChatRoom:成员取消固定成员
ChatRoomClose ChatRoom:聊天室被关闭了
ChatRoomInfoUpdated ChatRoom:聊天室信息被更新了
ChatRoomMemberKicked ChatRoom:成员被踢了
ChatRoomMemberTempMuteAdd ChatRoom: 新增临时禁言
ChatRoomMemberTempMuteRemove ChatRoom: 主动解除临时禁言
ChatRoomMyRoomRoleUpdated ChatRoom: 成员主动更新了聊天室内的角色信息(仅指nick/avator/ext)
ChatRoomQueueChange ChatRoom: 麦序队列中有变更
ChatRoomRoomMuted ChatRoom: 聊天室被禁言了,只有管理员可以发言,其他人都处于禁言状态
ChatRoomRoomDeMuted ChatRoom: 聊天室解除全体禁言状态

通知消息不仅可以判断通知类型,还携带不同的参数信息,由基类 NotificationAttachment 派生出针对不同通知消息类型的附件。

通知消息附件类型 说明
MemberChangeAttachment 群成员变动的通知消息实体。包括群成员变化和管理权限变化
MuteMemberAttachment MemberChangeAttachment 的子类,
群成员被禁言或者被取消禁言的通知消息实体
UpdateTeamAttachment 更新群资料的通知消息实体
LeaveTeamAttachment 成员离开讨论组的通知消息实体
DismissAttachment 解散群的通知消息实体
ChatRoomNotificationAttachment 聊天室通知消息实体
ChatRoomRoomMemberInAttachment ChatRoomNotificationAttachment 的子类,
进入聊天室通知消息实体
ChatRoomTempMuteRemoveAttachment ChatRoomNotificationAttachment 的子类,
解除聊天室禁言通知消息实体
ChatRoomQueueChangeAttachment ChatRoomNotificationAttachment 的子类,
聊天室队列变更通知消息实体
ChatRoomTempMuteAddAttachment ChatRoomNotificationAttachment 的子类,
添加聊天室禁言通知消息实体

1. 通知类消息有在线、离线、漫游。 2. 不计入消息未读数。 3. 没有通知栏提醒(如有需要,第三方自行实现)。

提示消息

提示消息(又叫做 Tip 消息)主要用于会话内的通知提醒,可以看做是自定义消息的简化,有独立的消息类型 MsgTypeEnum.tip 。 区别于自定义消息,Tip 消息暂不支持 setAttachment,如果要使用 Attachment 请使用自定义消息。 Tip 消息使用场景例如:进入会话时出现的欢迎消息,或是会话过程中命中敏感词后的提示消息等场景,当然也可以用自定义消息实现,只是相对复杂一些。

/**
 * 创建一条提示消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createTipMessage(String sessionId, SessionTypeEnum sessionType);
// 向群里插入一条Tip消息,使得该群能立即出现在最近联系人列表(会话列表)中,满足部分开发者需求。
Map<String, Object> content = new HashMap<>(1);
content.put("content", "成功创建高级群");
// 创建tip消息,teamId需要开发者已经存在的team的teamId
IMMessage msg = MessageBuilder.createTipMessage(teamId, SessionTypeEnum.Team);
msg.setRemoteExtension(content);
// 自定义消息配置选项
CustomMessageConfig config = new CustomMessageConfig();
// 消息不计入未读
config.enableUnreadCount = false;
msg.setConfig(config);
// 消息发送状态设置为success
msg.setStatus(MsgStatusEnum.success);
// 保存消息到本地数据库,但不发送到服务器
NIMClient.getService(MsgService.class).saveMessageToLocal(msg, true);

自定义消息

除了内建消息类型以外,SDK 还支持收发自定义消息类型。SDK 不负责定义和解析自定义消息的具体内容,解释工作由开发者完成。SDK 会将自定义消息存入消息数据库,会和内建消息一并展现在消息记录中。

为了使用更加方便,自定义消息采用附件的方式展示给开发者。体现在 IMMessage 类中,自定义消息的内容会被解析为 MsgAttachment 对象,由于 SDK 并不知道自定义消息的格式,第三方 App 需要注册一个自定义消息解析器。当第三方 App 调用查询查询消息历史的接口,或者是 SDK 收到消息通知第三方 App 时,就能将自定义消息内容转换为一个 MsgAttachment 对象,然后就可以同操作图片消息等带附件消息一样,操作自定义消息了。

与 IMMessage 一致,为了增加自定义消息的灵活性,SDK 还提供了对其生命周期和推送参数的一些配置选项,开发者可以自主设定一条自定义消息是否要保存云端消息记录,是否要漫游,发送时是否要多端同步等的配置选项 CustomMessageConfig

创建自定义消息原型


/**
 * 创建一条APP自定义类型消息, 同时提供描述字段,可用于推送以及状态栏消息提醒的展示。
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param content     消息简要描述,可通过IMMessage#getContent()获取,主要用于用户推送展示。
 * @param attachment  消息附件对象
 * @param config      自定义消息配置
 * @return 自定义消息
 */
public static IMMessage createCustomMessage(String sessionId, SessionTypeEnum sessionType, String content, MsgAttachment attachment, CustomMessageConfig config);

//或者:创建一条APP自定义类型消息并指定文件(图片、音视、视频...)上传(如果有)时使用的 nos scene
public static IMMessage createCustomMessage(String sessionId, SessionTypeEnum sessionType, String content, MsgAttachment attachment, CustomMessageConfig config, String nosTokenSceneKey)

在 demo 中,我们提供了一个用自定义消息实现的“剪刀石头布”的游戏,下面以此为例,详细解析其实现步骤。

首先,我们先定义一个自定义消息附件的基类,负责解析你的自定义消息的公用字段,比如类型等。还可以定义一些公共接口,用于一些便利性的调用。

注意: 实现 MsgAttachment 接口的成员都要实现 Serializable。

// 先定义一个自定义消息附件的基类,负责解析你的自定义消息的公用字段,比如类型等等。
public abstract class CustomAttachment implements MsgAttachment {

    // 自定义消息附件的类型,根据该字段区分不同的自定义消息
    protected int type;

    CustomAttachment(int type) {
        this.type = type;
    }

    // 解析附件内容。
    public void fromJson(JSONObject data) {
        if (data != null) {
            parseData(data);
        }
    }

    // 实现 MsgAttachment 的接口,封装公用字段,然后调用子类的封装函数。
    @Override
    public String toJson(boolean send) {
        return CustomAttachParser.packData(type, packData());
    }

    // 子类的解析和封装接口。
    protected abstract void parseData(JSONObject data);
    protected abstract JSONObject packData();
}

然后,继承这个基类,实现“剪刀石头布”的附件类型。注意,成员变量都要实现 Serializable。

public class GuessAttachment extends CustomAttachment {

    // 猜拳类型枚举
    public enum Guess {
        Shitou(1),
        Jiandao(2),
        Bu(3),
        ;
    }

    private Guess value;

    public GuessAttachment() {
        super(CustomAttachmentType.Guess);
        random();
    }

    // 解析猜拳类型具体数据
    @Override
    protected void parseData(JSONObject data) {
        value = Guess.enumOfValue(data.getIntValue("value"));
    }

    // 数据打包
    @Override
    protected JSONObject packData() {
        JSONObject data = new JSONObject();
        data.put("value", value.getValue());
        return data;
    }

    private void random() {
        int value = new Random().nextInt(3) + 1;
        this.value = Guess.enumOfValue(value);
    }
}

第三步,实现自定义消息的附件解析器。

public class CustomAttachParser implements MsgAttachmentParser {

    // 根据解析到的消息类型,确定附件对象类型
    @Override
    public MsgAttachment parse(String json) {
        CustomAttachment attachment = null;
        try {
            JSONObject object = JSON.parseObject(json);
            int type = object.getInteger("type");
            JSONObject data = object.getJSONObject(KEY_DATA);
            switch (type) {
                case CustomAttachmentType.Guess:
                    attachment = new GuessAttachment();
                    break;
                default:
                    attachment = new DefaultCustomAttachment();
                    break;
            }

            if (attachment != null) {
                attachment.fromJson(data);
            }
        } catch (Exception e) {

        }

        return attachment;
    }

    public static String packData(int type, JSONObject data) {
        JSONObject object = new JSONObject();
        object.put(KEY_TYPE, type);
        if (data != null) {
            object.put(KEY_DATA, data);
        }

        return object.toJSONString();
    }
}

最后,将该附件解析器注册到 SDK 中。为了保证生成历史消息时能够正确解析自定义附件,注册一般应放在 Application 的 onCreate 中 的主进程判断语句内完成。

NIMClient.getService(MsgService.class).registerCustomAttachmentParser(new CustomAttachParser()); // 监听的注册,必须在主进程中。

若需要发送文件类型消息,例如图片等,可以参考阅后即焚的实现。具体实现步骤如下:

第一步,定义一个自定义的附件类型,并继承 FileAttachment。注意,成员变量都要实现 Serializable。

public class SnapChatAttachment extends FileAttachment {

    private static final String KEY_PATH = "path";
    private static final String KEY_SIZE = "size";
    private static final String KEY_MD5 = "md5";
    private static final String KEY_URL = "url";

    public SnapChatAttachment() {
        super();
    }

    public SnapChatAttachment(JSONObject data) {
        load(data);
    }

    @Override
    public String toJson(boolean send) {
        JSONObject data = new JSONObject();
        try {
            // 重发使用本地路径
            if (!send && !TextUtils.isEmpty(path)) {
                data.put(KEY_PATH, path);
            }

            if (!TextUtils.isEmpty(md5)) {
                data.put(KEY_MD5, md5);
            }
            // 注意:这段代码一定要写。
            // SDK在调toJson的时候 父类FileAttachemnt的url才有值。
            // 这个值是sdk自动赋值的。
            data.put(KEY_URL, url);
            data.put(KEY_SIZE, size);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return CustomAttachParser.packData(CustomAttachmentType.SnapChat, data);
    }

    private void load(JSONObject data) {
        path = data.getString(KEY_PATH);
        md5 = data.getString(KEY_MD5);
        url = data.getString(KEY_URL);
        size = data.containsKey(KEY_SIZE) ? data.getLong(KEY_SIZE) : 0;
    }
}

第二步,实现自定义消息的附件解析器。

public class CustomAttachParser implements MsgAttachmentParser {

    // 根据解析到的消息类型,确定附件对象类型
    @Override
    public MsgAttachment parse(String json) {
        CustomAttachment attachment = null;
        try {
            JSONObject object = JSON.parseObject(json);
            int type = object.getInteger("type");
            JSONObject data = object.getJSONObject(KEY_DATA);
            switch (type) {
                case CustomAttachmentType.SnapChat:
                    return new SnapChatAttachment(data);
                default:
                    attachment = new DefaultCustomAttachment();
                    break;
            }

            if (attachment != null) {
                attachment.fromJson(data);
            }
        } catch (Exception e) {

        }

        return attachment;
    }
    ...
}

最后,将该附件解析器注册到 SDK 中。为了保证生成历史消息时能够正确解析自定义附件,注册一般应放在 Application 的 onCreate 中完成。

NIMClient.getService(MsgService.class).registerCustomAttachmentParser(new CustomAttachParser()); // 监听的注册,必须在主进程中。

为方便开发者参考,我们提供实现自定义消息的示例: Android自定义消息(类似微信骰子功能)

消息特殊设置

消息扩展字段

扩展字段分为服务器扩展字段( RemoteExtension )和本地扩展字段( LocalExtension ),最大长度 1024 字节。对于服务器扩展字段,该字段会发送到其他端,而本地扩展字段仅在本地有效。对于这两种扩展字段, SDK 都会存储在数据库中。示例如下:

IMMessage msg = MessageBuilder.createCustomMessage(...);
Map<String, Object> data = new HashMap<>();
data.put("key1", "ext data");
data.put("key2", 2015);
msg.setRemoteExtension(data); // 设置服务器扩展字段

NIMClient.getService(MsgService.class).sendMessage(msg, false);

利用扩展字段开发者可以实现一些特殊场景,例如群@功能:

1. 当发送群消息@某人的时候,可以通过扩展字段带上被@的账号列表发送出去;群成员收到群消息时,查看扩展字段的@账号列表里有没有自己,如果有,则界面上做被@的提醒。

2. 此外,最近会话列表项也提供扩展标签和扩展字段,可以满足开发者在最近会话列表中做@提醒的需求: 在监听到最新联系人有更新时,可以根据最近一条消息的 ID 查询出该条消息,查看扩展字段是否包含@账号列表,如果包含自己,可以在扩展字段上做一个标记,如果有新消息到来(未进入聊天界面查看时),要继续保持最近会话列表里面有@提醒,就可以参考扩展字段的标记,做界面的上被@的提醒。

3. 1.8.0 版本后消息提醒支持定制,开发者可以自行定制@消息在通知栏提醒时需要展示的文案。

消息属性设置详解

发送消息时可以设置消息配置选项 CustomMessageConfig,主要用于设定消息的声明周期,是否需要推送,是否需要计入未读数等。

自定义消息配置 CustomMessageConfig 属性

/**
 * 该消息是否要保存到服务器,如果为false,通过 MsgService#pullMessageHistory(IMMessage, int, boolean)拉取的结果将不包含该条消息。
 * 默认为true。
 */
public boolean enableHistory = true;

/**
 * 该消息是否需要漫游。如果为false,一旦某一个客户端收取过该条消息,其他端将不会再漫游到该条消息。
 * 默认为true
 */
public boolean enableRoaming = true;

/**
 * 多端同时登录时,发送一条自定义消息后,是否要同步到其他同时登录的客户端。
 * 默认为true
 */
public boolean enableSelfSync = true;

/**
 * 该消息是否要消息提醒,如果为true,那么对方收到消息后,系统通知栏会有提醒。
 * 默认为true
 */
public boolean enablePush = true;

/**
 * 该消息是否需要推送昵称(针对iOS客户端有效),如果为true,那么对方收到消息后,iOS端将显示推送昵称。
 * 默认为true
 */
public boolean enablePushNick = true;

/**
 * 该消息是否要计入未读数,如果为true,那么对方收到消息后,最近联系人列表中未读数加1。
 * 默认为true
 */
public boolean enableUnreadCount = true;

/**
 * 该消息是否支持路由,如果为true,默认按照app的路由开关(如果有配置抄送地址则将抄送该消息)
 * 默认为true
 */
public boolean enableRoute = true;

/**

// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
String text = "this is an example";
// 创建一个文本消息
IMMessage textMessage = MessageBuilder.createTextMessage(account, sessionType, text);

// 消息的配置选项
CustomMessageConfig config = new CustomMessageConfig();
// 该消息不保存到服务器
config.enableHistory = false;
// 该消息不漫游
config.enableRoaming = false;
// 该消息不同步
config.enableSelfSync = false;
textMessage.setConfig(config);

// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(textMessage, false);

反垃圾设置详解

云信反垃圾包括通用反垃圾以及易盾反垃圾,开通了易盾反垃圾的App,消息将优先转发到易盾进行反垃圾过滤。如果需要对当前消息进行特殊配置,Android 需要对即将发送的消息调用 IMMessagesetNIMAntiSpamOption 方法,传递参数为 NIMAntiSpamOption 对象,开发者需要构造这个 NIMAntiSpamOption 对象。支持的配置项目如下所示:

NIMAntiSpamOption 参数说明

NIMAntiSpamOption参数 说明
enable boolean,是否过易盾反垃圾,若App 并没有开通易盾,则设置无效。若设置为false,则此消息只过通用反垃圾
content String,自定义反垃圾字段,仅对自定义消息有效
antiSpamConfigId String,可选项,易盾反垃圾配置id,可以指定此消息过易盾某个反垃圾配置

content 必须传递 json 对象,长度不超过 5000 字节,格式如下

{
    "type": 1, //1:文本,2:图片,3视频
    "data": "" //文本内容or图片地址or视频地址
}
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
String text = "这条不需要过易盾,过通用反垃圾";
// 创建一个文本消息
IMMessage textMessage = MessageBuilder.createTextMessage(account, sessionType, text);

// 构造反垃圾对象
NIMAntiSpamOption antiSpamOption = new NIMAntiSpamOption();
antiSpamOption.enable = false;
textMessage.setNIMAntiSpamOption(antiSpamOption);

// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(textMessage, false);

消息接收

基本概念介绍

通过添加消息接收观察者 MsgServiceObserve#observeReceiveMessage,在有新消息到达时,第三方 APP 就可以接收到通知。该代码的典型场景为消息对话界面,在界面 onCreate 里注册消息接收观察者,在 onDestroy 中注销观察者。在收到消息时,判断是否是当前聊天对象的消息,如果是,加入到列表中显示。

/**
 * 注册/注销消息接收观察者。
 *     通知的消息列表中的消息不一定全是接收的消息,也有可能是自己发出去,比如其他端发的消息漫游过来,
 *     或者调用MsgService#saveMessageToLocal(IMMessage, boolean)后,notify参数设置为true,通知出来的消息。
 * @param observer 观察者, 参数为收到的消息列表,消息列表中的消息均保证来自同一个聊天对象。
 * @param register true为注册,false为注销
 */
public void observeReceiveMessage(Observer<List<IMMessage>> observer, boolean register);
Observer<List<IMMessage>> incomingMessageObserver =
    new Observer<List<IMMessage>>() {
        @Override
        public void onEvent(List<IMMessage> messages) {
            // 处理新收到的消息,为了上传处理方便,SDK 保证参数 messages 全部来自同一个聊天对象。
        }
    }
NIMClient.getService(MsgServiceObserve.class)
        .observeReceiveMessage(incomingMessageObserver, true);

文本消息接收

文本消息的接收比较简单,通过注册监听消息接口,接收消息即可,具体示例见上文。

多媒体消息接收

多媒体消息包括语音消息,图片消息,视频消息等带有文件附件的消息。通过 MsgServiceObserve#observeReceiveMessage 接收到消息时,需要下载文件附件,包含自动下载附件和手动下载附件两种方式,默认为自动下载。下载完成之后,才可以通过 IMMessage 获取到相应的文件路径。可以通过监听消息状态,来判断附件是否下载完成。

1. 附件参数说明

多媒体附件的基类是 FileAttachment,它的子类有原生消息类型中的:视频消息附件 VideoAttachment,图片消息附件 ImageAttachment, 音频消息附件 AudioAttachment,以及自定义消息的阅后即焚消息附件 SnapChatAttachment。

以 FileAttachment 为例,说明附件参数:

返回值 FileAttachment 接口 说明
String getPath() 获取文件本地路径,若文件不存在,返回 null。
语音消息的文件路径,在附件下载完成之后可以获取。
图片或视频的文件路径,需要手动下载之后获取,收到消息时,SDK 只默认自动下载缩略图文件
String getPathForSave() 获取用于保存该文件的位置
String getThumbPath() 获取缩略图文件的本地路径,若文件不存在,返回 null。
当消息状态显示缩略图下载完成之后,可获取缩略图文件路径
String getThumbPathForSave() 获取用于保存缩略图文件的位置
void setPath(String path) 设置文件路径
long getSize() 获取文件大小,单位为byte
void setSize(long size) 设置文件大小,单位为 byte
String getMd5() 获取文件内容 MD5
void setMd5(String md5) 设置文件内容 MD5
String getUrl() 获取文件在服务器上的下载 url。若文件还未上传,返回 null
void setUrl(String url) 设置文件在服务器上下载 url
String getExtension() 获取文件后缀名
void setExtension(String extension) 设置文件后缀名
String getFileName() 获取文件名
String getDisplayName() 获取文件的显示名。可以和文件名不同,仅用于界面展示
String setDisplayName() 设置文件显示名

2. 自动下载附件

如果接收到多媒体消息,SDK 默认会在后台自动下载附件。如果是语音消息,直接下载文件,如果是图片或视频消息,下载缩略图文件。

3. 手动下载附件

开发者可在 Application 初始化,配置 SDKOptions 时,将 SDKOptions.preloadAttach 设置为 false 来关闭自动下载,并在用户翻阅到对应消息,再通过以下代码手动下载。

/**
 * 正常情况收到消息后附件会自动下载。如果下载失败,可调用该接口重新下载
 *
 * @param msg   附件所在的消息体
 * @param thumb 下载缩略图还是原文件。为true时,仅下载缩略图。
 *              该参数仅对图片和视频类消息有效
 * @return AbortableFuture 调用跟踪。可设置回调函数,可中止下载操作
 */
public AbortableFuture<Void> downloadAttachment(IMMessage msg, boolean thumb);
参数 说明
msg 附件所在的消息体
thumb 下载缩略图还是下载原文件。为 true 时,仅下载缩略图。该参数仅对图片和视频类消息有效
// 下载之前判断一下是否已经下载。若重复下载,会报错误码414。(以SnapChatAttachment为例)
private boolean isOriginImageHasDownloaded(final IMMessage message) {
    if (message.getAttachStatus() == AttachStatusEnum.transferred &&
        !TextUtils.isEmpty(((SnapChatAttachment) message.getAttachment()).getPath())) {
        return true;
    }
    return false;
}
// 下载附件,参数1为消息对象,参数2为是下载缩略图还是下载原图。
// 因为下载的文件可能会很大,这个接口返回类型为 AbortableFuture ,允许用户中途取消下载。
AbortableFuture future = NIMClient.getService(MsgService.class).downloadAttachment(message, true);

说明:错误码414可能是重复下载,或者下载参数错误。

4. 监听消息状态

通过监听消息状态的变化,来查看消息是否下载完成。这个接口可以监听消息接收或发送状态 MsgStatusEnum 和 消息附件接收或发送状态 AttachStatusEnum 的变化。当状态更改为 AttachStatusEnum.transferred 表示附件下载成功。

/**
 * 注册/注销消息状态变化观察者
 * @param observer 观察者, 参数为改变的消息体,更改的状态可能包含status和attachStatus
 * @param register true为注册,false为注销
 */
public void observeMsgStatus(Observer<IMMessage> observer, boolean register);

MsgStatusEnum 属性说明

MsgStatusEnum 属性说明 说明
draft 草稿
sending 正在发送中
success 发送成功
fail 发送失败
read 消息已读,
发送消息时表示对方已看过该消息,
接收消息时表示自己已读过,
一般仅用于音频消息
unread 未读状态

AttachStatusEnum 属性说明

AttachStatusEnum 属性说明 说明
def 默认状态,未开始
transferring 正在传输
transferred 传输成功, 附件下载成功标志
fail 传输失败
// 监听消息状态变化
NIMClient.getService(MsgServiceObserve.class).observeMsgStatus(statusObserver, register);

private Observer<IMMessage> statusObserver = new Observer<IMMessage>() {
        @Override
        public void onEvent(IMMessage msg) {
            // 1、根据sessionId判断是否是自己的消息
            // 2、更改内存中消息的状态
            // 3、刷新界面
        }
    };

消息重发

消息发送失败之后,可以重发消息。消息重发和消息发送是同一个接口,只是第二个重发参数的设置不同,当 resend 参数为 true 时,表示重发消息。具体示例请参考消息发送。

/**
 * 发送消息。<br>
 * @param msg    带发送的消息体,由{@link MessageBuilder}构造
 * @param resend 如果是发送失败后重发,标记为true,否则填false
 * @return InvocationFuture 可以设置回调函数。消息发送完成后才会调用,如果出错,会有具体的错误代码。
 */
public InvocationFuture<Void> sendMessage(IMMessage msg, boolean resend);

已读回执

网易云通信提供点对点消息的已读回执。注意:此功能仅对 P2P 消息中有效。

在会话界面中调用发送已读回执的接口并传入最后一条消息,即表示这之前的消息都已读,对端将收到此回执。

发送已读回执

发送消息已读回执的一般场景:

1. 进入 P2P 聊天界面(如果没有收到新的消息,反复进入调用发送已读回执接口, SDK 会自动过滤,只会发送一次给网易云通信服务器)。

2. 处于聊天界面中,收到当前会话新消息时。

/**
* 发送消息已读回执
* @param sessionId 会话ID(聊天对象账号)
* @param message 已读的消息(一般是当前接收的最后一条消息)
*/
NIMClient.getService(MsgService.class).sendMessageReceipt(sessionId, message);
// 该帐号为示例,请先注册
String account = "testAccount";

// message为会话中已读的最后一条消息
NIMClient.getService(MsgService.class).sendMessageReceipt(account, message);

监听已读回执

监听到已读回执,根据 IMMessage 中的 isRemoteRead() 方法来判断该条消息是否已读,并刷新界面。

一般场景:一个会话显示一个已读回执(比该已读回执对应的消息时间早的消息都是已读),那么刷新界面时,可以倒序查找第一条 isRemoteRead 接口返回 true 的消息打上已读回执标记。注意:当删除带有已读回执标记的消息时,也应该刷新界面。

/**
 * 注册/注销消息已读回执观察者
 * @param observer 观察者, 参数为已读回执集合。
 * @param register true为注册,false为注销
 */
public void observeMessageReceipt(Observer<List<MessageReceipt>> observer, boolean register);
返回值 MessageReceipt接口 说明
String getSessionId() 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
long getTime() 该会话最后一条已读消息的时间,比该时间早的消息都视为已读
// 注册/注销观察者
NIMClient.getService(MsgServiceObserve.class).observeMessageReceipt(messageReceiptObserver, register);

private Observer<List<MessageReceipt>> messageReceiptObserver = new Observer<List<MessageReceipt>>() {
        @Override
        public void onEvent(List<MessageReceipt> messageReceipts) {
            receiveReceipt();
        }
    };

消息过滤

支持单聊和群聊的通知类型消息过滤,支持音视频类型消息过滤。

通知消息是指 IMMessage#getMsgType 为 MsgTypeEnum#notification。 SDK 在 2.4.0 版本后支持上层指定过滤器决定是否要将通知消息,音视频消息存入 SDK 数据库(并通知上层收到该消息)。请注意,注册过滤器的时机,建议放在 Application 的 onCreate 中, SDK 初始化之后。

/**
 * 注册通知消息过滤器
 *
 * @param filter 上层实现的通知消息过滤器,决定是否过滤通知消息(不存储到数据库中),传 null 表示注销(取消)通知消息过滤器
 */
void registerIMMessageFilter(IMMessageFilter filter);
返回值 IMMessageFilter接口 说明
boolean shouldIgnore(IMMessage message) 是否过滤通知消息。
返回 true 表示过滤(那么 SDK 将不存储此消息,上层也不会收到此消息),
默认 false 即不过滤(默认存储到 db 并通知上层)

SDK 过滤群头像变更通知和过滤音视频类型消息。

// 在 Application启动时注册,保证漫游、离线消息也能够回调此过滤器进行过滤。注意,过滤器的实现不要有耗时操作。
NIMClient.getService(MsgService.class).registerIMMessageFilter(new IMMessageFilter() {
        @Override
        public boolean shouldIgnore(IMMessage message) {
            if (UserPreferences.getMsgIgnore() && message.getAttachment() != null) {
                if (message.getAttachment() instanceof UpdateTeamAttachment) {
                    UpdateTeamAttachment attachment = (UpdateTeamAttachment) message.getAttachment();
                    for (Map.Entry<TeamFieldEnum, Object> field : attachment.getUpdatedFields().entrySet()) {
                        if (field.getKey() == TeamFieldEnum.ICON) {
                            return true; // 过滤
                        }
                    }
                } else if (message.getAttachment() instanceof AVChatAttachment) {
                    return true; // 过滤
                }
            }
            return false; // 不过滤
        }
    });

消息转发

网易云通信支持消息转发功能,不支持通知消息和音视频消息以及机器人消息的转发,其他消息类型均支持。

首先,通过 MessageBuilder 创建一个待转发的消息,参数为想转发的消息,转发目标的聊天对象id, 转发目标的会话类型。然后,通过 MsgService#sendMessage 接口,将消息发送出去。

/**
 * 创建一条待转发的消息
 *
 * @param message     要转发的消息
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @return 待转发的消息
 */
public static IMMessage createForwardMessage(IMMessage message, String sessionId, SessionTypeEnum sessionType);
参数 说明
message 要转发的消息
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// forwardMessage为待转发的消息, 一般由上下文获得
IMMessage message = MessageBuilder.createForwardMessage(forwardMessage, account, sessionType);

// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(textMessage, false);

消息撤回

发起消息撤回

网易云通信支持发送消息的撤回。超过限定时间(默认时间为 2 分钟)的撤回会失败,并返回错误码508。消息撤回后,未读数不发生变化,通知栏提醒的文案变为:撤回了一条消息。

几种不能撤回的情况:

1. 消息为空,不能撤回 2. 消息没有发送成功,不能撤回 3. 消息发起者不是自己,不能撤回 4. 会话 sessionId 是自己,不能撤回

/**
 *  消息撤回
 *
 * @param message 待撤回的消息
 * @return InvocationFuture 可设置回调函数,监听发送结果。
 */
public InvocationFuture<Void> revokeMessage(IMMessage message);
// message表示待撤回的消息
NIMClient.getService(MsgService.class).revokeMessage(message).setCallback(new RequestCallback<Void>() {
    @Override
    public void onSuccess(Void param) {
        // 撤回消息成功,可执行界面相关操作
    }

    @Override
    public void onFailed(int code) {
        if (code == ResponseCode.RES_OVERDUE) {
           // 发送时间超过2分钟的消息,不能被撤回
        } else {
           // 其他错误code
        }
    }

    @Override
    public void onException(Throwable exception) {

    }
});

监听消息撤回

消息被撤回,会收到消息撤回通知。当开发者收到消息撤回通知,可以在界面上做相应的消息删除等操作。

/**
 * 注册/注销消息撤回的观察者
 * @param observer 观察者,参数为被撤回的消息。
 * @param register true为注册,false为注销
 */
public void observeRevokeMessage(Observer<RevokeMsgNotification> observer, boolean register);
方法 说明
getMessage() 获取撤回消息
getRevokeAccount() 获取撤回者
Observer<RevokeMsgNotification> revokeMessageObserver = new Observer<RevokeMsgNotification>() {
    @Override
    public void onEvent(RevokeMsgNotification notification) {
       // 监听到消息撤回的通知,可以在界面做相应的操作
       // 获取撤回消息
       notification.getMessage();
       // 获取撤回者
       notification.getRevokeAccount();
    }
};

NIMClient.getService(MsgServiceObserve.class).observeRevokeMessage(revokeMessageObserver, true);

监听全员广播消息

网易云通信支持全员广播消息,支持离线,没有推送。广播消息由服务端接口发起,对应用内的所有用户发送一条广播。客户端不支持发送, SDK 收到广播之后直接往上层通知,不存储。建议开发者注册观察者与应用生命周期一致。

/**
 * 注册/注销全员广播消息观察者
 * @param observer 观察者,参数全员广播消息。
 * @param register true为注册,false为注销
 */
public void observeBroadcastMessage(Observer<BroadcastMessage> observer, boolean register);
参数 说明
id 广播id
fromAccount 广播发送者账号
time 广播消息时间戳
content 广播消息内容
/**
 * 注册云信全服广播接收器
 *
 * @param register
 */
private void registerNimBroadcastMessage(boolean register) {
    NIMClient.getService(MsgServiceObserve.class).observeBroadcastMessage(new Observer<BroadcastMessage>() {
        @Override
        public void onEvent(BroadcastMessage broadcastMessage) {
            // 处理
        }
    }, register);
}

客户端本地反垃圾

客户端本地反垃圾功能是利用本地的一个垃圾词库进行垃圾词检测,反垃圾词库由开发者在云信后台管理配置,SDK 内部负责管理和加载这个词库。垃圾词命中之后,可以选择后续的操作规则,现在支持三种规则:

规则由在词库中指定。

开发者使用此功能的步骤:

验证本地反垃圾

/**
 * 检验本地反垃圾词库
 * @param content 需要检查的文本
 * @param replacement 指定 content 中被反垃圾词库命中后的替换文本
 * @return LocalAntiSpamResult 检验结果
 */
public LocalAntiSpamResult checkLocalAntiSpam(String content, String replacement);
参数 说明
operator 命中的垃圾词操作类型,0:未命中;1:客户端替换;2:客户端拦截;3:服务器拦截;
content 将垃圾词替换后的文本

String content = "美国"
String replacement = "河蟹"

LocalAntiSpamResult result = NIMClient.getService(MsgService.class).checkLocalAntiSpam(content,replacement);

int operator = result.getOperator();
if (operator == 0){
    // 未命中
}else if(operator == 1){
    // 客户端替换
    content = result.getContent();
}else if(operator == 2){
    // 客户端拦截
    // 就不要发消息了
}else if(operator == 3){
    // 交给服务器
    // 在消息中设置命中属性
    message.setClientAntiSpam(true);
}