消息相关
更新时间: 2024/03/14 16:36:35
本文介绍 NIM SDK 消息和会话相关的问题及其解决方法。
如何接收 IM 消息?
- 点对点消息和群聊消息都通过监听
MsgServiceObserve.observeReceiveMessage
回调获取,两者通过会话类型进行区分SessionTypeEnum
。 通过NIMClient.getService(MsgServiceObserve.class).observeReceiveMessage(incomingMessageObserver, true)
注册消息接收观察者,新消息到达时会收到通知,其中包含IMMessage
的列表。 - 聊天室消息通过监听
ChatRoomServiceObserver.observeReceiveMessage
回调获取。 通过NIMClient.getService(ChatRoomServiceObserver.class).observeReceiveMessage(Observer> observer, true)
注册聊天室消息接察者,新消息到达时会收到通知,其中包含ChatRoomMessage
的列表。
点对点消息、群聊消息以及聊天室消息通过同一个回调 NIMChatManagerDelegate
的 onRecvMessages:
回调获取,三者通过会话类型进行区分 NIMSessionType
。
通过 - (void)onRecvMessages:(NSArray *)messages
注册消息接收观察者,新消息到达时会收到通知,其中包含 NIMMessage
的列表。
- 点对点消息和群聊消息都通过通过
nim_talk_reg_receive_cb
注册接收消息全局回调,或者通过nim_talk_reg_receive_msgs_cb
注册接收批量消息回调(一个会话为单位),指定接收消息的回调函数nim_talk_receive_cb_func
,收到的content
中包含消息内容kNIMMsgKeyLocalReceiveMsgContent
。两者通过会话类型进行区分kNIMMsgKeyToType
。
如果是图片、语音消息,SDK会自动下载,然后通过注册的nim_nos_reg_download_cb
下载全局回调中指定的回调函数nim_nos_download_cb_func
进行通知。 - 通过
nim_chatroom_reg_receive_msg_cb
注册接收聊天室消息的全局回调,指定接收聊天室消息的回调函数nim_chatroom_receive_msg_cb_func
,其中包含聊天室ID和消息内容。
- 点对点消息和群聊消息都通过初始化 SDK 时
NIM.getInstance
中onmsg
注册的回调函数进行通知,其中包含消息对象。两者通过会话类型进行区分scene
。 - 聊天室消息通过初始化聊天室时
Chatroom.getInstance
中onmsgs
注册的回调函数onChatroomMsgs
进行通知,其中包含聊天室消息数组。
如何设置和获取 IM 消息的扩展字段?
单聊或群聊消息具有服务端扩展字段和客户端扩展字段。聊天室消息没有客户端扩展字段。
服务端扩展字段只能在消息发送前设置,会同步到其他端;客户端扩展字段在消息发送前后设置均可,不会同步到其他端。
- 扩展字段,请使用JSON格式封装,并传入非格式化的JSON字符串,最大长度1024字节。
- 设置消息的本地扩展字段之后,必须调用相应的更新方法,否则无法生效。
调用以下接口时,参数ext
表示消息的扩展字段。
https://api.netease.im/nimserver/msg/sendMsg.action
:发送普通消息https://api.netease.im/nimserver/msg/sendBatchMsg.action
:批量发送点对点普通消息https://api.netease.im/nimserver/chatroom/sendMsg.action
:发送聊天室消息
-
设置消息的扩展字段:
- 单聊和群聊消息:构造
IMMessage
对象时,通过setRemoteExtension
方法设置消息的服务端扩展字段,通过setLocalExtension
方法设置本地客户端扩展字段。 - 聊天室消息:
ChatRoomMessage
继承自IMMessage
,通过setRemoteExtension
方法设置聊天室消息的服务端扩展字段。
- 单聊和群聊消息:构造
-
更新消息的扩展字段: 通过
NIMClient.getService(MsgService.class).updateIMMessage(IMMessage message)
方法更新消息的本地扩展字段。 -
获取消息的扩展字段:
- 单聊和群聊消息:通过
IMMessage
对象的getRemoteExtension()
方法获取消息的服务端扩展字段,通过getLocalExtension()
方法获取消息的本地扩展字段。 - 聊天室消息:通过
ChatRoomMessage
对象的getRemoteExtension()
方法获取聊天室消息的服务端扩展字段。
- 单聊和群聊消息:通过
-
设置消息的扩展字段:发送消息时,构造的
NIMMessage
对象中,通过remoteExt
属性设置消息的服务端扩展字段,通过localExt
属性设置消息的本地客户端扩展字段(聊天室消息的本地客户端扩展字段不生效)。 -
更新消息的扩展字段:通过
[[NIMSDK sharedSDK].conversationManager updateMessage:(NIMMessage *)message forSession:(NIMSession *)session completion:(nullable NIMUpdateMessageBlock)completion]
方法更新消息的本地扩展字段。 -
获取消息的扩展字段:通过
NIMMessage
对象的remoteExt
属性获取消息的服务端扩展字段,通过localExt
属性获取消息的本地扩展字段。
-
设置消息的扩展字段:
- 单聊和群聊消息:调用
nim_talk_send_msg
接口发送点对点或群聊消息时,参数json_msg
表示消息结构Json Keys,其中kNIMMsgKeyServerExt = "server_ext"
表示消息的服务端扩展字段,kNIMMsgKeyLocalExt = "local_ext"
表示消息的本地客户端扩展字段。 - 聊天室消息:调用
nim_chatroom_send_msg
接口发送聊天室消息时,参数msg
表示消息结构Json Keys,其中kNIMChatRoomMsgKeyExt = "ext"
表示聊天室消息的服务器扩展字段。
- 单聊和群聊消息:调用
-
更新消息的扩展字段: 通过
nim_msglog_update_localext_async
方法更新消息的本地扩展字段,参数msg_id
表示消息id,参数local_ext
表示消息本地扩展字段内容。 -
获取消息的扩展字段:
- 单聊和群聊消息:消息结构Json Keys中,
kNIMMsgKeyServerExt = "server_ext"
表示消息的服务端扩展字段,kNIMMsgKeyLocalExt = "local_ext"
表示消息的本地扩展字段。 - 聊天室消息:消息结构Json Keys中,
kNIMChatRoomMsgKeyExt = "ext"
表示聊天室消息的服务器扩展字段。
- 单聊和群聊消息:消息结构Json Keys中,
- 设置消息的扩展字段:
- 单聊和群聊消息:发送消息时,构造的消息对象中,
custom
字段表示消息的服务端扩展字段。 注意:目前设置localCustom
本地客户端扩展字段不生效。 - 聊天室消息:发送聊天室消息时,构造的聊天室消息对象中,
custom
字段表示聊天室消息的服务端扩展字段。
- 单聊和群聊消息:发送消息时,构造的消息对象中,
- 更新消息的扩展字段:
通过如下方法更新消息的本地扩展字段。
nim.updateLocalMsg({ idClient: msg.idClient, localCustom: '{"key","value"}', done: updateLocalMsgDone })
- 获取消息的扩展字段:
- 单聊和群聊消息:通过
IMMessage
对象的custom
字段获取消息的服务端扩展字段,通过localCustom
字段获取消息的本地扩展字段。 - 聊天室消息:通过
ChatroomMessage
对象的custom
字段获取聊天室消息的服务端扩展字段。
- 单聊和群聊消息:通过
如何处理消息丢失(收不到)的问题?
单聊消息和群聊消息丢失
- 请先确定是历史消息没有收到,还是实时消息没有收到。
- 历史消息包括离线消息和漫游消息,这两种消息存在数量限制,请确认是否超限,具体请查看:消息概述。
- 如果是实时消息没有收到,请继续查看下文。
- 检查是否存在多端登录的情况,如果多端登录的情况下,消息配置了不需要多端同步,则只有一端可以收到消息。
- 检查发送方和接收方账号是否登录成功。
- 检查接收方是否处于非登录状态(例如掉线、自动重连失败、被踢等)。
- 检查消息是否正确设置接收方。
- 检查发送方是否发送成功。
- 检查接收方是否触发消息通知。
- 对于点对点聊天,如果配置了非好友不允许发消息,客户端 SDK 向非好友发消息会返回 403。
- 对于点对点聊天,如果被拉黑之后继续发送消息,对方是无法接收的。
- 拉黑是单向的。当A被B拉黑后,A不能给B发消息(但是B还能给A发)。
- 当A被B拉黑后,A给B发消息将返回错误码 7101。如果选择重发,为了避免开发者循环重试以及方便上层做UI展示,SDK会返回状态码200,但是这条消息实际上仍然不会发给A。但是该消息带有漫游属性,A其他设备能漫游下发。
- 如果配置了非好友不允许发消息,被拉黑后发消息将返回403(不再返回7101)。
- 如果是群聊,检查接收方是否为群成员。
- 检查是否命中了反垃圾。关于IM反垃圾,请查看内容审核。
- 检查是否配置了第三方回调,拒绝了消息投递。关于第三方回调,请查看回调说明。
聊天室消息丢失
- 检查发送方和接收方账号是否登录成功。
- 检查聊天室消息是否正确设置接收方。
- 检查聊天室消息发送方是否发送成功。
- 检查接收方是否在聊天室内。
- 检查接收方是否触发消息通知。
- 检查是否命中了反垃圾。关于IM反垃圾,请查看内容审核。
- 匿名进入聊天室,只能收消息不能发消息,发消息会返回403,聊天室其他成员是收不到的。
如何查看消息设置的接收方?
单聊和群聊消息:
调用 MessageBuilder
的各种创建消息的方法时,参数sessionId
表示聊天对象ID(消息接收方)。
如果是点对点消息,sessionId
就是对方 accid
;如果是群聊,sessionId
就是群组ID teamId
。
调用接口[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error]
时,session
表示消息接收方。
如果是点对点消息,session
就是对方 accid
;如果是群聊,session
就是群组ID teamId
。
调用接口 nim_talk_send_msg
时,json_msg
参数的消息结构Json Keys中,kNIMMsgKeyToAccount
表示消息接收方。
如果是点对点消息,kNIMMsgKeyToAccount
就是对方 accid
;如果是群聊,kNIMMsgKeyToAccount
就是群组ID kNIMTeamInfoKeyID
。
通过nim
的各种send方法创建的消息对象中,to
字段表示消息接收方。
如果是点对点消息,to
就是对方 accid
;如果是群聊,to
就是群组ID teamId
。
聊天室消息:
调用接口NIMClient.getService(ChatRoomService.class).sendMessage(ChatRoomMessage msg, boolean resend)
发送聊天室消息时,确保通过ChatRoomMessageBuilder
接口创建的各种消息对象中,包含的roomId
参数配置正确。
调用接口[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error]
发送聊天室消息时,确保其中构造的会话对象NIMSession
的会话类型sessionType
为NIMSessionTypeChatroom
,sessionId
配置为正确的聊天室roomId
。
调用接口nim_chatroom_send_msg (const int64_t room_id, const char *msg, const char *json_extension)
发送聊天室消息时,确保room_id
参数配置正确。
通过chatroom
的各种send
方法发送聊天室消息时,可以通过chatroom.getChatroom(options)
方法获取聊天室信息,通过获取成功的回调函数来获取聊天室id。
如何判断 IM 消息发送成功?
单聊和群聊消息:
调用消息发送接口时,设置回调函数:NIMClient.getService(MsgService.class).sendMessage(IMMessage msg, boolean resend).setCallback(callback)
,判断是否进入onSuccess
回调。
调用消息发送接口[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error]
时,判断error == nil
表示方法调用成功;实现NIMChatManagerDelegate
的–sendMessage:didCompleteWithError:
方法,判断error == nil
表示消息已经发送至服务器。
发送消息状态通过登录前注册的全局回调nim_talk_reg_ack_cb
获取,判断result
中“发送消息回执Json Keys”的kNIMSendAckKeyMsgId
对应的kNIMSendAckKeyRescode
是否为kNIMResSuccess = 200
。
在消息发送的done
回调中根据消息对象的idClient
字段和error
对象来确定消息的发送状态,error
为空表示消息发送成功。
聊天室消息:
调用接口NIMClient.getService(ChatRoomService.class).sendMessage(ChatRoomMessage msg, boolean resend).setCallback(new RequestCallback<Void>() {})
发送聊天室时,如果回调onSuccess
表示发送成功。
调用接口[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error]
发送聊天室消息时,判断error == nil
表示方法调用成功;实现NIMChatManagerDelegate
的–sendMessage:didCompleteWithError:
方法,判断error == nil
表示消息已经发送至服务器。
调用接口nim_chatroom_send_msg
发送聊天室消息时,发送消息状态通过进入聊天室前注册的全局回调nim_chatroom_reg_send_msg_ack_cb (const char *json_extension, nim_chatroom_sendmsg_arc_cb_func cb, const void *user_data)
获取,回调函数typedef void(*nim_chatroom_sendmsg_arc_cb_func) (int64_t room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
中,error_code
如果为kNIMResSuccess = 200
表示发送成功。
通过chatroom
的各种send
方法发送聊天室消息时,可以通过设置的回调函数done: sendChatroomMsgDone
中error
参数获取发送结果,如果!error
没有错误表示发送成功。
如何在单聊/群聊消息发送成功后获取消息的内容?
当 NIM SDK 发送消息成功后后,若需要获取发送的消息内容,可以通过以下方法实现。
- 通过接口
NIMClient.getService(MsgServiceObserve.class).observeMsgStatus(Observer<IMMessage> observer, true)
注册消息状态变化观察者,该接口可以监听消息发送状态变化中回调IMMessage
对象。 - 通过
IMMessage
对象的getDirect()
方法获取消息方向,通过getSessionId()
方法获取聊天对象的accid/群组id,通过getContent()
方法获取文本消息具体内容,通过getAttachment()
方法获取文件消息附件对象,通过getStatus()
方法获取消息收发状态,通过getTime()
方法获取消息时间(单位为毫秒)。
- 实现
NIMChatManagerDelegate
的-sendMessage:didCompleteWithError:
方法来接收发送消息完成回调,其中回调NIMMessage
对象。可以通过isOutgoingMsg
属性判断是否是发送出去的消息。 - 通过
session
属性获取聊天对象的accid/群组id/聊天室id,通过text
属性获取消息文本(仅适用于文本消息和提示消息),通过messageObject
属性获取消息附件内容,通过deliveryState
属性获取发送消息的投递状态,通过timestamp
属性获取消息发送时间。
- 通过全局回调
nim_talk_reg_ack_cb
注册发送消息结果回调函数nim_talk_ack_cb_func
,回调result
为发送消息回执Json Keys,其中kNIMSendAckKeyMsgId = "msg_id"
表示客户端消息id。 - 通过
nim_msglog_query_msg_by_id_async
根据消息id查询本地单条消息,回调函数nim_msglog_query_single_cb_func
的result
为消息结构Json Keys。 其中,kNIMMsgKeyToAccount = "to_accid"
表示消息接收方accid/群组id,kNIMMsgKeyBody = "msg_body"
表示消息正文,kNIMMsgKeyAttach = "msg_attach"
表示消息附件,kNIMMsgKeyTime = "time"
表示消息时间戳,kNIMMsgKeyLocalLogStatus = "log_status"
表示消息状态NIMMsgLogStatus
。
- 在发送消息接口的
done
回调中返回IMMessage
对象。 - 通过
flow
属性获取消息方向,通过to
属性获取聊天对象的accid/群组id,通过text
属性获取文本消息的内容,通过file
属性获取文件消息的文件对象,通过status
属性获取消息发送状态,通过time
属性获取消息时间戳。
如何对自定义消息进行安全通检测?
自定义消息比较特殊,开发者如需自定义消息经由安全通反垃圾,在发送消息时需要通过上述相关字段进行配置,说明自定义消息的内容是文本还是图片、视频等,以便安全通按照相应类型进行反垃圾处理。开发配置中,如果配置了不经过安全通反垃圾,则消息会经过通用反垃圾。
通过useYidun
来选择该条消息是否经过安全通,通过配置antispam
, antispamCustom
来设置自定义消息的反垃圾类型。具体请参考发送消息中的具体参数。
调用 NIMMessage
的 setNIMAntiSpamOption
方法,传递参数为 NIMAntiSpamOption
对象,开发者需要构造这个 NIMAntiSpamOption
对象。其中,content
参数用于说明自定义消息的内容是文本还是图片,以便安全通按照相应类型进行内容安全检测处理。具体请参考安全通文档。
需要对即将发送的消息 NIMMessage
的 antiSpamOption
参数传递 NIMAntiSpamOption
对象,开发者需要构造这个 NIMAntiSpamOption
对象。其中,content
参数用于说明自定义消息的内容是文本还是图片,以便安全通按照相应类型进行内容安全检测处理。具体请参考安全通文档。
需要对即将发送的消息的 kNIMMsgKeyAntiSpamEnable
, kNIMMsgKeyAntiSpamContent
, kNIMMsgKeyAntiSpamUsingYiDun
参数进行配置,具体请参考安全通文档。
需要对即将发送的消息的 yidunEnable
, antiSpamContent
, antiSpamUsingYidun
参数进行配置。具体请参考消息配置选项。
如果开通安全通,消息抄送会携带yidunRes
字段,并通过其中action
字段标记检测结果,0:通过,1:嫌疑,2:不通过 (只有yidunBusType为0或2时,抄送时才有此字段)。
yidunBusType 枚举:0:安全通文本反垃圾业务;1:安全通图片反垃圾业务;2:用户资料反垃圾业务;3:用户头像反垃圾业务。
对应的图片返回labels字段, 文本类反垃圾参考labels字段的释义、 图片类反垃圾参考labels字段的释义。
如何获取最近会话列表?
获取最近会话只查询客户端本地信息。服务端接口无法获取最近会话。
最近会话不会漫游,但是其中的消息可以漫游。在另一台设备上接收漫游消息后,也会产生最近会话。
服务器会下发最近1个月内最多100个会话的已读回执,客户端本地会增量更新。
调用接口NIMClient.getService(MsgService.class).queryRecentContacts().setCallback(new RequestCallbackWrapper>() {...}
时,在onResult
中回调最近会话列表(最近联系人列表)。
当有消息收发时,SDK 会通知最近会话更新,需要通过接口NIMClient.getService(MsgServiceObserve.class).observeRecentContact(Observer<java.util.List<RecentContact>> observer, true);
注册最近会话列表变化观察者,onEvent
中会回调消息列表。
调用接口 NSArray *recentSessions = [NIMSDK sharedSDK].conversationManager.allRecentSessions
获取最近会话列表。
提前监听NIMConversationManagerDelegate
中的相应回调方法,当增加、修改、删除最近会话时,SDK 提供对应的回调通知。
-
增加最近会话的回调:
-didAddRecentSession:totalUnreadCount:
-
修改最近会话的回调:
-didUpdateRecentSession:totalUnreadCount:
-
删除最近会话的回调:
-didRemoveRecentSession:totalUnreadCount:
-
最近会话全部加载完成的回调:
NIMConversationManagerDelegate
的-didLoadAllRecentSessionCompletion
必须同时满足以下条件,该回调才会执行。
- SDK 版本为7.0.0及以上。
- 需要设置
NIMSDKConfig
的asyncLoadRecentSessionEnabled
属性为YES
。 - 本地会话很多,处理耗时较长的情况下:SDK正在查询会话 > 开发者调用
allRecentSessions
> 此时SDK尚未返回全部会话 > SDK查询完全部会话 > SDK回调-didLoadAllRecentSessionCompletion
> 开发者再次调用allRecentSessions
获取全量会话并更新UI。 - 本地会话较少的情况下,当开发者调用
allRecentSessions
时,如果SDK已经返回全部会话,则不会触发该回调。
调用nim_session.h 中nim_session_query_all_recent_session_async
接口获取最近会话列表,其中 nim_session_query_recent_session_cb_func
注册回调函数,回调的result
为“会话列表的Json Keys”(相关定义在nim_session_def.h中)。
当有消息收发时,SDK 会通知最近会话更新,需要通过接口nim_session_reg_change_cb
注册全局最近会话列表项变更通知,其中nim_session_change_cb_func
为回调函数,回调的result
为“会话列表的Json Keys”。
根据浏览器是否支持数据库、开发者是否开启数据库的不同,获取到的最近会话不同。
-
浏览器支持数据库时, SDK 会将会话存储于本地数据库中,并且在初始化
NIM.getInstance
时通过回调onsessions
将最多 100 条会话列表返回给开发者,可以调用接口nim.getLocalSessions(options)
获取更多最近会话。注意:第一次调用
nim.getLocalSessions
时,不要传入lastSessionId
字段。 -
浏览器不支持数据库,或者初始化时配置了禁用数据库,则 SDK 无法在本地维护数据库,最近会话只能依赖消息产生。初始化
NIM.getInstance
时,配置会话更新回调函数onupdatesession
来接收更新会话的回调,包括发送和接收消息、设置当前会话,重置会话未读数等。
如何实现未读数清零
- 如果当前正在聊天的会话,不需要显示未读数,必须清零。
- 如果非当前会话,只有当用户希望主动对某个会话或者全部会话进行清零的时候,才调用接口进行清零。
- 调用接口
NIMClient.getService(MsgService.class).setChattingAccount(account, sessionType)
设置当前会话,SDK 会自动管理消息的未读数。- 该接口会自动调用
clearUnreadCount(account, sessionType)
将正在聊天对象的未读数清零。如果有新消息到达,且消息来源是正在聊天的对象,将不会有消息提醒,其未读数也不会递增。 - 如果收到不是当前聊天对象的新消息,SDK 则会将消息来源的未读数自动累加,并通过开发者注册的观察者接口
NIMClient.getService(MsgServiceObserve.class).observeRecentContact(observer, register)
进行通知。 - 如果需要不进入聊天窗口就将未读数清零,可以调用接口
NIMClient.getService(MsgService.class).clearUnreadCount(account, sessionType)
来实现。 - 退出聊天界面或离开最近联系人列表界面,需要调用接口
NIMClient.getService(MsgService.class).setChattingAccount(MsgService.MSG_CHATTING_ACCOUNT_NONE, SessionTypeEnum.None)
来恢复更新未读数(以及通知栏消息提醒)。
- 该接口会自动调用
- 调用接口
NIMClient.getService(MsgService.class).clearAllUnreadCount()
设置所有会话消息为已读(所有联系人的未读数清零)。
调用该接口后,会触发MsgServiceObserve.observeRecentContact(observer, register)
通知。
- 调用接口
[[NIMSDK sharedSDK].conversationManager markAllMessagesReadInSession:(NIMSession *)session]
将一个会话中的所有消息标记为已读。如果是当前会话,每次收到消息都需要调用该接口。 - 调用接口
-markAllMessagesRead
设置所有会话消息为已读(所有联系人的未读数清零)。
- 调用接口
nim_session_set_unread_count_zero_async
设置所有会话消息为已读(所有联系人的未读数清零)。如果是当前会话,每次收到消息都需要调用该接口。 - 调用接口
nim_msglog_batch_status_read_async
来批量设置未读消息为已读状态。
- 调用接口
nim.setCurrSession('sessionId')
设置当前会话。将此会话未读数置为0,并且此会话收到消息不再更新未读数。
Web SDK的sessionId
,根据会话类型的不同,包含不同的前缀(p2p/team
),例如:p2p-accid
,team-teamid
。 - 调用接口
nim.resetCurrSession()
来重置当前会话。重置当前会话后, 所有会话在收到消息之后正常更新未读数。
此接口和nim.setCurrSession
组合起来使用,相当于一对开关。nim.setCurrSession
将会话未读数置为0并且不再更新未读数,而nim.resetCurrSession()
用来恢复更新未读数。 - 调用接口
nim.resetSessionUnread('sessionId')
重置会话未读数。将此会话未读数置为0,之后收到消息重新计算未读数。 - 调用接口
nim.resetAllSessionUnread()
来重置内存中的所有会话未读数。之后收到消息重新计算未读数。
9.1.0 及以下版本下,如果开发者在同一浏览器、不同标签页、(开启多端登录)同时登录同一账号,那么请在初始化SDK时配置db: false
,以免多标签页共用同一数据库时造成未读数异常等情况。
:::
如何设置和获取最近会话的扩展字段?
- 最近会话是客户端本地概念,因此只支持本地扩展字段,没有服务端扩展字段,也不会同步到其他客户端。
- 最近会话的扩展字段请使用JSON格式封装,并传入非格式化的JSON字符串。扩展字段可以用于实现@提醒等功能。
设置最近会话的扩展字段:
通过最近会话对象 RecentContact
的setExtension(Map extension)
方法设置最近会话的扩展字段。
注意:如果希望持久化该字段(保存到数据库中),请调用NIMClient.getService(MsgService.class).updateRecent(RecentContact recent)
或者updateRecentAndNotify(RecentContact recent)
更新会话属性(写入数据库)。这两个方法的区别在于后者会触发会话更新的通知。
调用接口-(void)updateRecentLocalExt:(nullable NSDictionary *)ext recentSession:(NIMRecentSession *)recentSession
设置最近会话的本地扩展,其中参数ext
表示扩展字段,开发者需要确保 NSDictionary 可以转换为JSON。
通过接口nim_session_set_extend_data
设置会话项扩展数据,其中参数data
表示最近会话的扩展字段。
通过接口nim.updateLocalSession({id:..., localCustom: '{"key", "value"}', done:...})
更新本地会话时,其中的localCustom
字段表示最近会话的扩展字段。
注意: 如果浏览器不支持数据库,或者对应的会话不存在,SDK都算更新成功。
获取最近会话的扩展字段:
通过RecentContact
最近会话对象的getExtension()
方法获取最近会话的扩展字段。
通过NIMRecentSession
最近会话对象的localExt
属性获取最近会话的扩展字段。
- 通过接口
nim_session_query_all_recent_session_async
查询会话列表时,回调函数nim_session_query_recent_session_cb_func
的result
为会话列表的Json Keys。 - 注册
nim_session_reg_change_cb
最近会话列表项变更通知全局回调,回调函数nim_session_change_cb_func
的result
为会话列表的Json Keys。
以上两个回调函数的会话列表Json Keys中,kNIMSessionExtendedData = "extend_data"
表示最近会话的扩展字段。
通过Session
对象的localCustom
属性获取最近会话的扩展字段。
收到消息时,如何获取用户资料?
-
接收到单聊/群聊消息时,只能通过消息体本身获取到发送者的账号和昵称,其他资料需要通过用户账号(accid)再查询。
-
接收到聊天室消息时,只能通过消息体本身获取到发送者的账号,通过聊天室消息扩展获取到发送者的昵称和头像地址,其他资料需要通过用户账号(accid)再查询。
收到消息时,会得到IMMessage
列表,可以通过NIMMessage
的getFromAccount()
和 getFromNick
方法获取发送者的 accid 和昵称,再通过getFromNick()
方法获取发送方的昵称。
收到消息时,会得到 NIMMessage
列表,可以通过NIMMessage
对象的from
字段获取发送者的 accid,通过 senderName
字段获取发送者的昵称(注意是发送者当前的昵称而不是发送消息时的昵称)。
通过接收消息的回调函数 nim_talk_receive_cb_func
获取消息内容content
,消息结构 Json Keys 中,kNIMMsgKeyFromAccount
表示发送者的 accid,kNIMMsgKeyFromNick
表示发送者的昵称。
收到消息时,在onmsg
回调函数中得到IMMessage
消息对象,其中from
参数表示发送者的accid,fromNick
表示发送者的昵称。
如何对文本消息进行关键字搜索?
IM SDK 支持对客户端本地数据库中保存的文本消息进行关键字搜索。
通过 NIMClient.getService(MsgService.class).searchMessageHistory(String keyword, List<String> fromAccounts, IMMessage anchor, int limit).setCallback(new RequestCallbackWrapper<List<IMMessage>>(){ ... })
方法进行搜素,其中参数keyword
表示搜索关键字。
支持基于 Lucene 的全文检索插件,具体请参见开发文档。
通过NIMConversationManager
中的–searchMessages:option:result:
方法或–searchAllMessages:result:
方法进行搜索,在参数option
(NIMMessageSearchOption
)中指定searchContent
进行关键字搜索。
- C: 通过
nim_msglog_query_msg_by_options_async
方法进行搜索,通过参数search_content
指定搜索关键字。 - C++:通过
nim::MsgLog::QueryMsgByOptionsAsync
方法进行搜索,通过参数search_content
指定搜索关键字。 - C#:通过
NIM.Messagelog.MessagelogAPI.QueryMsglogByCustomCondition
方法进行搜索,通过参数searchContent
指定搜索关键字。
在开启数据库并且浏览器支持数据库的情况下,可以通过nim.getLocalMsgs
方法进行搜索,传入参数keyword
进行关键字搜索。
如何开启未读数多端同步?
开启会话的未读数多端同步后,在一个端已读的会话在其它端也会被标记为已读。
客户端批量处理会话未读数,一次最多 10 条。如果要清理未读数的会话较多,又希望正常同步,可以分开多次批量清理,每次不超过 10 条。
默认关闭未读数多端同步。
可以通过设置SDKOptions
对象的sessionReadAck
属性为 true 实现。
默认关闭未读数多端同步。
可以通过设置[NIMSDKConfig sharedConfig].shouldSyncUnreadCount = YES
实现。
默认开启未读数多端同步。
可以通过调用 nim_client_init
初始化,在初始化的json_extension
扩展参数中,配置kNIMSyncSessionAck
为 true 实现。
默认关闭未读数多端同步。
可以通过调用NIM.getInstance
初始化,在初始化参数中配置syncSessionUnread: true
实现。
如何在本地插入一条消息?
IM SDK 支持插入消息到本地数据库,但不发送到服务端,主要使用于在App中保存本地提醒类消息。
自 7.0.0 版本起,IM SDK 支持插入本地消息时设置发送方,以便本地展示(例如客服咨询场景中自动发送欢迎语),但实际上对方并未发送此消息。
通过以下方法往本地消息历史数据库里写入一条消息(如果已存在这条消息,则更新该条消息)。
- 调用
NIMClient.getService(MsgService.class).saveMessageToLocal(IMMessage msg, boolean notify)
方法或NIMClient.getService(MsgService.class).saveMessageToLocalEx(IMMessage msg, boolean notify, long time)
方法保存消息到本地数据库,但不发送到服务器。如需通知到 UI,可将入参notify
设置为true
,调用成功后,会触发MsgServiceObserve.observeReceiveMessage(Observer, boolean)
通知。 - 调用
NIMClient.getService(MsgService.class).insertLocalMessage(IMMessage msg, String fromAccount)
方法在本地数据库插入一条消息,可以通过参数fromAccount
设置消息发送方的 accid,此接口会通知 UI,即调用成功后,会触发MsgServiceObserve.observeReceiveMessage(Observer, boolean)
通知。。
通过NIMConversationManager
协议的 –saveMessage:forSession:completion:
方法来写入本地消息,方法为异步写入,无须开发者在上层单独开线程,直接在当前线程调用即可。
构造NIMMessage
消息对象时,可以通过from
字段设置消息发送者的accid,通过timestamp
字段设置保存消息的时间。
注意:不允许插入已存在的消息。当保存消息成功之后,会收到NIMChatManagerDelegate
中的onRecvMessages:
回调。
- C: 通过nim_msglog.h中定义的
nim_msglog_insert_msglog_async
方法插入本地消息。其中参数need_update_session
表示是否更新会话列表(一般最新一条消息会有更新的需求)。参数json_msg
中的kNIMMsgKeyFromAccount = "from_id"
表示消息发送方accid,kNIMMsgKeyTime = "time"
表示消息时间戳。 - C++: 通过
nim::MsgLog::WriteMsglogToLocalAsync
方法插入本地消息,通过msg
的sender_accid_
属性指定发送者accid,通过timetag_
属性指定消息时间戳。示例代码请点此查看。
发送消息时可以指定参数isLocal
为true
,那么 SDK 并不会发送此条消息,而是直接调用回调表示发送成功,并更新对应的会话。
可以通过localFrom
参数设置发送方的accid,通过time
参数设置保存消息的时间。
如何对应用内用户批量发送消息?
IM SDK 客户端不支持批量发送消息。可以通过以下服务端 API 来实现对应用内用户批量发送消息:
- 批量发送单聊消息
https://api.netease.im/nimserver/msg/sendBatchMsg.action
,可以发送文本、图片、语音、视频、地理位置和自定义消息。- 最多可批量给 500 个用户账号发送单聊消息。如果批量提供的帐号中有未注册的帐号,会提示并返回给用户。
- 单个应用最高调用频率 120 次/分。如超限,将报错(状态码:416),并且应用将被屏蔽 1 分钟,之后才可再次调用。
- 发送广播消息
https://api.netease.im/nimserver/msg/broadcastMsg.action
,可以对应用内的所有用户发送广播消息。
如何配置发送消息的参数?
以下各属性对应的配置项,若无特殊说明,默认值为真(true/1/YES
)。
- 必须在控制台开启消息漫游开关,消息漫游才会生效。
- 必须在控制台开启并配置消息抄送,抄送才会生效。
类型 | 服务端发送消息option | Windows发送消息Json Keys | Android发送消息CustomMessageConfig | iOS发送消息NIMMessageSetting | Web发送消息IMMessage属性 |
---|---|---|---|---|---|
漫游 | roam | kNIMMsgKeyMsgRoaming = "roam_msg" | enableRoaming | roamingEnabled | isRoamingable |
存云端历史 | history | kNIMMsgKeyHistorySave = "cloud_history" | enableHistory | historyEnabled | isHistoryable |
多端同步 | sendersync | kNIMMsgKeyMsgSync = "sync_msg" | enableSelfSync | syncEnabled | isSyncable |
推送/提醒 | push | kNIMMsgKeyPushEnable = "push_enable" | enablePush | apnsEnabled | isPushable |
推送带昵称 | needPushNick | kNIMMsgKeyPushEnable = "push_enable" | enablePushNick | apnsWithPrefix | needPushNick |
* 抄送 | route | kNIMMsgKeyMsgRoutable = "routable_msg" | enableRoute | routeEnabled | cc |
计入未读 | badge | kNIMMsgKeyPushNeedBadge = "push_need_badge" | enableUnreadCount | shouldBeCounted | isUnreadable |
存离线消息 | persistent | kNIMMsgKeySetMsgOffline = "offline_msg" | * 默认存离线 | * 默认存离线 | isOfflinable |
如何获取消息附件上传下载进度?
通过NIMClient.getService(MsgServiceObserve.class).observeAttachmentProgress(Observer observer, true)
来注册消息附件上传/下载进度观察者。通过AttachmentProgress
的getTotal()
方法获取文件总长度,getTransferred()
获取已经传输的字节数,getUuid()
方法获取附件对应的消息的uuid。
通过uuid查询对应消息的方法为:
- 异步版本:
NIMClient.getService(MsgService.class).queryMessageListByUuid(uuids)
- 同步版本:
NIMClient.getService(MsgService.class).queryMessageListByUuidBlock(uuids)
- 上传进度: 通过
NIMChatManagerDelegate
的-sendMessage:progress:
方法的progress
参数获取附件上传进度。 - 下载进度: 通过
NIMChatManagerDelegate
的-fetchMessageAttachment:progress:
方法的progress
参数获取附件下载进度。
- 上传进度: 调用
nim_talk_send_msg()
发送消息时,配置上传进度的回调函数nim_nos_upload_prg_cb_func
,如果发送的消息里包含了文件资源,则通过此回调函数通知上传进度。回调的uploaded_size
表示已上传数据大小,file_size
表示文件大小。 - 下载进度: SDK默认自动下载附件,没有进度回调,在附件下载完成后才会触发收到消息的回调。
- 可以在调用
nim_client_init
初始化SDK时,通过json_extension
参数中配置kNIMPreloadAttach = "preload_attach"
为false
关闭自动下载。 - 调用
nim_nos_download_media
进行手动下载时,配置下载进度的回调函数nim_nos_download_prg_cb_func
。回调的downloaded_size
表示已下载数据大小,file_size
表示文件大小。
- 可以在调用
- 上传进度: 调用
nim.sendFile
发送文件消息时,通过uploadprogress
参数配置上传进度回调函数,例如uploadprogress: function(obj)
。其中回调对象obj
的total
参数表示文件总大小字节数,loaded
参数表示已上传的字节数,percentage
参数表示上传进度(格式为数字,例如80),percentageText
参数表示上传进度文本(格式为百分比,例如80%)。 - 下载进度: 无。
如何获取会话未读数?
- 调用
NIMClient.getService(MsgService.class).getTotalUnreadCount()
方法获取总未读数。 - 调用
RecentContact
的getUnreadCount()
方法获取指定会话的未读数。
- 调用
NIMConversationManager
的allUnreadCount
方法获取总未读数。 - 调用
NIMRecentSession
的unreadCount
属性获取指定会话的未读数。
通过全局注册nim_session_reg_change_cb
来监听最近会话变更。回调函数nim_session_change_cb_func
中,total_unread_counts
参数表示总未读数,result
参数表示[会话列表的Json Keys],其中kNIMSessionUnreadCount = "unread_count"
参数表示对应于会话kNIMSessionId = "id"
的未读数。
只能在主线程操作获取未读数。
- 会话(
Session
)对象的unread
值为会话的未读数。 - 总未读数没有直接获取的接口,需要遍历各会话未读数进行累加。
如何设置缩略图动图?
IM SDK 发送动态图片时,服务器生成的默认缩略图为获取动态图片第一帧作为静态图片。
若要服务器生成缩略图动图,需要在客户端设置支持对缩略动图。
调用NIMClient.init
或NIMClient.initSDK
初始化时,配置初始化参数中的SDKOptions
对象的animatedImageThumbnailEnabled
属性为true
。
调用[[NIMSDK sharedSDK] registerWithOption:option]
初始化 SDK 之前,配置[NIMSDKConfig sharedConfig].animatedImageThumbnailEnabled = YES
。
发送图片消息时,NIMImageObject
的–initWithImage:
方法只支持 JPEG 和 PNG 两种格式,动图(GIF、WebP)需要通过–initWithFilepath:
或–initWithData:extension:
方法进行初始化。
- C: 调用
nim_client_init
初始化时,在初始化参数json_extension
中,kNIMAnimatedImageThumbnailEnabled = "animated_image_thumbnail_enabled"
表示对缩略图动图的支持,将其设置为true
。 - C++: 调用
nim::Client::Init
初始化时,在初始化参数config
中,animated_image_thumbnail_enabled_
表示对缩略图动图的支持,将其设置为true
。 - C#: 调用
NIM.ClientAPI.Init
初始化时,在初始化参数config
的CommonSetting
中,AnimatedImageEnabled
表示对缩略图动图的支持,将其设置为true
。
暂不支持生成缩略图动图。
如何标记本地消息为已读?
某些应用场景下,需要在本地将消息标记为已读,用于显示不同的业务状态。
例如,对于收到的语音消息,未读时默认显示一个小红点提示,如果已读则将消息标记为本地已读来取消小红点提示。
调用 IMMessage
的setStatus(MsgStatusEnum.read)
方法。对于发送的消息,表示对方已读;对于接收的消息,表示自己已读,一般仅用于音频消息。
目前read只是用在了语音消息上,自己播放了就会变为read。对方播放后,本端是无感知的。
注意:此接口只是临时修改内存中的消息状态,如果需要持久化该状态到数据库中,还需要调用NIMClient.getService(MsgService.class).updateIMMessageStatus(message)
方法。
设置NIMMessage
的isPlayed
属性为YES
表示消息被播放过(对于聊天室消息无效)。
nim_msglog_set_sub_status_async
设置消息子状态,参数msglog_sub_status
类型为NIMMsgLogSubStatus
消息子状态枚举值,kNIMMsgLogSubStatusPlayed
表示已播放。nim_msglog_set_status_async
设置消息状态,参数msglog_status
类型为NIMMsgLogStatus
消息状态枚举值,kNIMMsgLogStatusRead
表示收到消息并且已读。nim_msglog_batch_status_read_async
批量设置未读消息为已读状态。
由于 Web 浏览器不一定支持本地数据库,因此可能导致状态丢失从而表现不符合预期,所以SDK不提供相关接口。如果开发者确实需要在 Web 端实现,建议从应用层自行处理。
如何过滤消息?
IM SDK 提供消息过滤忽略的功能。消息过滤后,SDK 将不存储对应的消息,也不会上抛给接收回调,因此应用层不会收到对应的消息。
- 下文提到的通知消息属于一种特殊的消息,而不是系统通知。通知消息和系统通知的区别,请参见开发文档。
- 消息过滤对在线消息、离线消息、漫游消息等有效。查询本地和云端历史记录目前只有移动端是支持的,PC和Web端无法过滤。
- 不要在消息过滤函数或方法中进行耗时操作,否则将导致线程阻塞。
- 目前不支持过滤聊天室消息。
建议在Application的onCreate中,SDK初始化之后,注册过滤器。
调用 NIMClient.getService(MsgService.class).registerIMMessageFilter(new IMMessageFilter() { public boolean shouldIgnore(IMMessage message) {...}})
方法注册过滤器,返回true
表示过滤。
在初始化 SDK 时,创建一个NIMSDKConfigDelegate
对象,实现下列方法,并将此对象赋值给NIMSDKConfig
的delegate
属性。
-(BOOL)shouldIgnoreNotification:(NIMNotificationObject *)notification
方法,返回YES
表示忽略某条通知消息。-(BOOL)shouldIgnoreMessage:(NIMMessage *)message
方法,返回YES
表示忽略某条普通消息。
目前 IM SDK PC端只支持过滤群通知消息,不支持过滤其他类型的消息。建议在 SDK 初始化之后,登录之前,注册过滤器。
通过nim_talk_reg_notification_filter_cb
注册接收群通知消息是否需要过滤的全局回调,其中参数nim_talk_team_notification_filter_func
表示群通知消息是否需要过滤的函数定义。
在初始化SDK时,配置以下参数:
- 配置
shouldIgnoreNotification
参数,表示是否需要忽略某条通知消息。该参数类型为函数(function),接收一个通知消息对象,如果该函数返回true
,那么SDK将忽略此条通知消息。 - 配置
shouldIgnoreMsg
参数,表示是否要忽略某条普通消息。该参数类型为函数(function),接收一个消息对象,如果该函数返回true
,那么SDK将忽略此条消息。
如何删除历史消息和漫游消息?
调用下列接口成功后,服务器将清空此刻之前保存的对应聊天对象的历史消息或漫游消息(只影响自己不影响对方)。
- 如果应用没有在云信控制台开启消息漫游,调用下列接口会返回403。
- 对于点对点会话,还可以在清空会话历史的同时清空漫游消息。
- SDK 支持从服务器上清空某一点对点会话的历史和漫游消息。清空后,自己不能再查询到清空时间点之前和对方的聊天消息。清空漫游消息后,服务器无法再下发清空时间点之前该会话的漫游消息。
- 删除后只影响自己,不影响对方的历史和漫游消息。例如,A用户清空和B用户的会话记录之后,调用服务端 API 查询历史记录时,如果
from
设置为A的情况下,查询结果为空;但是from
设置为B的情况下,可以正常查询到结果。
- 调用
https://api.netease.im/nimserver/msg/delRoamSession.action
API 删除漫游消息。 - 调用
https://api.netease.im/nimserver/msg/delMsg.action
API 删除指定消息(会删除对应的离线消息、漫游消息和历史消息)。
- 调用
NIMClient.getService(MsgService.class).deleteRoamingRecentContact(contactId, sessionTypeEnum).setCallback(new RequestCallback<Void>() { ... })
方法删除指定会话的漫游消息。 - 调用
NIMClient.getService(MsgService.class).clearServerHistory(java.lang.String accid, SessionTypeEnum sessionType)
方法删除指定会话的历史消息,默认清空漫游消息。 - 调用
NIMClient.getService(MsgService.class).clearServerHistory(java.lang.String accid, SessionTypeEnum sessionType, boolean deleteRoam)
方法删除历史消息,可设置同时删除漫游消息或仅删除历史下消息。
- 调用
NIMConversationManager
的-(void)deleteRemoteSessions:(NSArray *)sessions completion:(nullable NIMRemoveRemoteSessionBlock)completion
方法删除指定会话的漫游消息。调用成功后,当前会话之前的消息都不会漫游到其他端。 - 调用
NIMConversationManager
的–deleteSelfRemoteSession:option:completion:
方法删除云端历史消息。若不设置删除选项,服务端默认会同时删除漫游消息。
调用以下接口删除会话的云端历史消息和漫游消息。
- C:
nim_msglog_delete_history_online_async
- C++:
MsgLog::DeleteHistoryOnlineAsync
- C#:
NIM.Messagelog.MessagelogAPI.DeleteHistoryOnlineAsync
调用 clearServerHistoryMsgsWithSync
方法删除指定会话的云端历史消息和漫游消息,其中,scene
表示消息场景,包括:'p2p'
、'team'
。
- 当配置
scene: 'p2p'
时,to
表示对方的accid。 - 当配置
scene: 'team'
时,to
表示群ID。
注意:调用成功后,本地数据库中指定会话的消息也会被删除,但是数据库中会话会保留,若需要删除数据库中的会话,可以调用 deleteLocalSession
删除。
如何查询指定消息类型的历史消息?
- 查询云端记录:
- 调用
NIMClient.getService(MsgService.class).pullMessageHistoryExType(IMMessage anchor, long toTime, int limit, QueryDirectionEnum direction, MsgTypeEnum[] msgTypes)
方法,通过设置msgTypes
参数查询指定消息类型的历史消息。该接口查询结果不存本地数据库。 - 调用
NIMClient.getService(MsgService.class).pullMessageHistoryExType(IMMessage anchor, long toTime, int limit, QueryDirectionEnum direction, MsgTypeEnum[] msgTypes, boolean persist)
方法,通过设置msgTypes
参数查询指定消息类型的历史消息,设置persist
参数指定是否将查询结果存入本地数据库。
- 调用
- 查询本地记录:
- 调用
NIMClient.getService(MsgService.class).queryMessageListByType(MsgTypeEnum msgTypeEnum, IMMessage anchor, int limit)
方法,通过设置msgTypeEnum
参数查询指定消息类型的历史消息。 - 调用
NIMClient.getService(MsgService.class).queryMessageListByTypes(java.util.List types, IMMessage anchor, long toTime, QueryDirectionEnum direction, int limit, boolean asc)
方法,通过设置types
参数查询指定消息类型的历史消息。
- 调用
- 查询云端记录:调用
NIMConversationManager
的–fetchMessageHistory:option:result:
方法,通过设置(NIMHistoryMessageSearchOption *)option
的messageTypes
参数查询指定消息类型的历史消息。从7.2.0版本开始,NIMHistoryMessageSearchOption
中新增参数syncMessageTypes
,表示是否将查询结果存入本地数据库。7.2.0(不含)之前的版本,查询结果不存本地数据库。 - 查询本地记录:调用
NIMConversationManager
的–searchMessages:option:result:
和–searchAllMessages:result:
方法,通过设置(NIMMessageSearchOption *)option
的messageTypes
参数查询指定消息类型的历史消息。
- 查询云端记录:调用
MsgLog::QueryMsgOnlineAsync(const QueryMsgOnlineAsyncParam & param, const QueryMsgCallback & cb)
,通过设置QueryMsgOnlineAsyncParam
的msg_type_list_
参数查询指定消息类型的历史消息。 - 查询本地记录:调用
MsgLog::QueryMsgByOptionsAsync
,通过设置msg_type
参数查询指定消息类型的历史消息。
- 查询云端记录:调用
{nim.getHistoryMsgs({..., msgTypes: [...], ...})}
方法,通过设置 StringArray 类型的msgTypes
参数来查询指定消息类型的历史消息。
支持并开启本地数据库时,可以调用nim.saveMsgsToLocal({msgs: msgs, done: err => (...)})
将查询结果存入本地数据库。 - 查询本地记录:调用
nim.getLocalMsgs(...)
方法,通过设置 String 类型的type
参数,或者StringArray类型的types
参数来查询指定消息类型的历史消息。
查询本地记录仅限开启indexedDB数据库时适用。
如何处理文件消息的URL?
IM SDK 支持发送图片、音频、视频等文件消息,SDK会将文件上传到网易云信存储服务器中,生成相应的URL。有些场景下需要对这些URL进行处理,例如:生成缩略图、裁剪图片、将音频转为mp3等。
- Windows开发指南页面有介绍获取图片缩略图和获取视频封面的方法。
- Web开发指南页面有相关的工具方法介绍。
- 还可以参考网易云的相关介绍:链式处理、管道、图片缩略、图片裁剪、图片旋转、图片质量转换、图片格式转换、图片渐进显示、音频格式转换、视频截图。
在这些处理URL的方法中,使用到URL拼接。
-
拼接说明
进行URL拼接时:
- 其中的英文问号(
?
)用于分隔原始地址和各种参数(地址串中只能有一个问号) &
用于连接多个参数(地址串中可以有多个&
)
也就是说,当只有一个参数时,在地址后面加英文问号和参数即可;当有多个参数时,不同的参数之间要用
&
隔开。 - 其中的英文问号(
-
拼接示例
- 例如原始地址为:https://nim.nosdn.netease.com/abcde/12345,添加一个
vframe
参数,可以拼接为https://nim.nosdn.netease.com/abcde/12345**?vframe** - 如果原始地址已经带有参数(已经包含问号),例如:https://nim.nosdn.netease.com/abcde/12345?createTime=1560150506,再添加一个
vframe
参数时,不能再使用问号,而应使用&
,拼接为https://nim.nosdn.netease.com/abcde/12345?createTime=1560150506**&vframe**
- 例如原始地址为:https://nim.nosdn.netease.com/abcde/12345,添加一个
-
场景应用
- 收到附件类消息(例如:图片、音频、视频、文件等),打开附件url进行下载时的默认文件名是该文件的随机编码值并且没有文件格式后缀,可以在url后拼接
download
字段来指定下载文件名,例如:https://nim-nosdn.netease.im/MTAxMTAwMg%3D%3D%2FbmltYV8yMDAzODU3NjRfMTU3NDY4NDkwNzI4Nl9hOTRkOWJjNy0xMzNiLTRlMDItYmZmNi1hY2Q4N2E0NzkyMTU%3D?**download=test.txt**
- 开发者还可以在收到附件类消息时,获取附件的文件名和文件格式后缀,用来拼接到url中,即可实现下载时生成和发送时一致的文件名。当然,也可以按照自定义规则来生成指定的文件名。
- 收到附件类消息(例如:图片、音频、视频、文件等),打开附件url进行下载时的默认文件名是该文件的随机编码值并且没有文件格式后缀,可以在url后拼接
如何单向删除消息?
自 7.4.0 版本开始,IM SDK 支持单向删除消息,仅支持删除一年内的消息。此功能需要联系技术支持开通。
- 用户单向删除一条消息成功后,该账号不能再从服务端拉取到该条消息。而且,登录此账号的其他端也会删除该消息的本地记录。除操作账号外,消息相关的其他账号不受该操作影响。
- 单向删除消息后,会产生一个通知,通知也会漫游,漫游范围是10天150条单向删除通知。单向删除通知不属于漫游消息,不受消息漫游开关的影响。
- 单向删除的消息中,漫游消息被删除的上限是150条。如果出现新设备会漫游到自己删除过的消息, 请检查是否删除的消息过多,删除的150条之后的消息还在漫游时间内,会在新设备上漫游到。
调用 NIMClient.getService(MsgService.class).deleteMsgSelf(IMMessage msg, String ext).setCallback(...)
方法单向删除消息。
调用 deleteMessageFromServer:ext:completion:
方法单向删除消息。
调用以下方法单向删除消息:
- C:
nim_msglog_delete_message_self_async
- C++:
MsgLog::DeleteMessageSelfAsync
调用 deleteMsgSelf(options: { custom?: string; msg: NIMMessage; done?: any })
方法单向删除消息。
如何接收快捷评论?
通过注册快捷评论事件监听接收快捷评论消息。
- 快捷评论支持点对点和高级群,不支持聊天室。
- 当评论这条消息后,仅有发送方可以收到推送。
- 快捷评论不支持服务端抄送。
监听 observeAddQuickComment
事件回调,回调中会返回接收到的快捷评论。
监听 onRecvQuickComment
事件回调,回调中会返回接收到的快捷评论。
监听 RegAddQuickCommentNotify
事件回调,回调中会返回接收到的快捷评论。
监听 onQuickComment
事件回调,回调中会返回接收到的快捷评论。
如何根据msgId等信息查询历史消息?
如果您删除本地某条消息,需要查询该消息下所有子消息。 例如:该条消息已经被回复,需要到回复详情页使用这条消息(msgId)来查询所有子消息。
调用 pullHistoryById
方法根据消息 ID 批量查询历史消息,主要用于消息回复场景。
调用 fetchHistoryMessages:syncToDB:completion:
方法根据消息 ID 批量查询历史消息,主要用于消息回复场景。
调用 QueryMessageOnline()
方法根据消息 ID 等信息查询历史消息,主要用于消息回复场景。
调用 nim.getMsgsByIdServer(options, done)
方法根据消息 ID 等信息查询历史消息,主要用于消息回复场景。
查询选项options
参数中的reqMsgs
数组的每个元素都是一个对象,包含scene、from、to、idServer、time
五个字段,数组长度最大为100。
为什么获取到的历史消息数小于设置的limit?
IM SDK 在获取云端历史记录时会根据客户端消息 ID 进行过滤再上抛,如果存在客户端重发消息等情况会导致客户端消息 ID 重复,通过 InvocationFuture 回调的数据会根据 msgid_client字段去重,去重之后获取到的云端历史记录可能就少于设置的 limit 值。
为什么Web端在撤回场景下获取到的lastmsg不准确?
IM Web 端不开启数据库时,对端撤回消息,Web 端无法得知上一条消息是什么。可以理解为在该情况下 SDK 能力有限。
建议在撤回消息时发一条 tips 消息来覆盖 lastmsg ,内容示例:“您撤回了一条消息”。
如何通过线上URL构建视频等附件消息?
注意:缩略图需要业务层单独处理,可以通过URL进行拼接,详情请参考如何处理文件消息的URL。
- 构建文件 URL:
NIMFileObject.setUploadURL:
- 构建音频 URL:
NIMAudioObject.setUploadURL:
- 构建视频 URL:
NIMVideoObject.setUploadURL:
- 构建图片 URL:
NIMImageObject.setUploadURL:
如何填写分页查询消息接口中的excludeMsgId参数?
excludeMsgId
字段通常时在分页查询时需要传入的,需要把上一页的最后一条消息的 serverId 传入该字段。获取到的消息的顺序是按照reverse
字段来排序的,如果reverse
相同的情况下,顺序是固定的。
excludeMsgId
字段的主要作用就是帮您在分页的时候去重的,比如说您有3条时间戳相同的消息A/B/C,您第一页中如果已经拉到了A和B两条消息,这个时候您查询下一页的时候需要把excludeMsgId传为消息B的msgid,这样的话新的这一个分页中只会返回消息C(没有消息A和B)。
excludeMsgId
传入的这条消息和之前的都是不会返回的。相同排序的情况下,顺序是固定的。 在前一页已经返回了2条消息的情况下(新到旧返回A/B, 旧到新返回C/B),这个时候查询下一页excludeMsgId都传B就行。
如何在发消息时透传易盾的反作弊检测参数?
在发云信IM消息的时候,可以传入一些易盾的扩展字段(反作弊检测参数等),展现在易盾智能审核平台 – 内容查询,如下图易盾控制台展示:
设置方法如下所示:
- Android:
IMMessage#setYidunAntiCheating(String)
- iOS:
IMMessage#yidunAntiCheating
- 服务端:
msg/sendMsg.action yidunAntiCheating
上述接口的传参格式是Json,且字符限制1024,具体格式参考易盾的反垃圾防刷版专属字段
示例:我需要传房间extLon1、extLon2
{"extension":" {"extLon1":740,"extLon2":6097} "}
如何实现云端消息检索?
自 IM SDK 8.5.0 版本起,支持云端历史记录文本关键字检索,云端历史记录需要在控制台开通才可以使用,并且开通前的历史记录将不会被检索到。
调用 InvocationFuture>pullMessageHistory(MsgFullKeywordSearchConfig config);
方法,其中MsgFullKeywordSearchConfig
的keyword
参数表示搜索关键字。
调用 - (void)retrieveServerMessages:(NIMMessageFullKeywordSearchOption *)option result:(nullable NIMRetrieveServerMessagesBlock)result;
方法进行搜素,其中NIMMessageFullKeywordSearchOption
的keyword
参数表示搜索关键字。
调用 void nim::MsgLog::FullTextSearchOnlineAsync ( const FullTextSearchOnlineAsyncParam & param,const FullTextSearchOnlineAsyncCallback & cb)
方法进行搜素,其中FullTextSearchOnlineAsyncParam
的keyword
参数表示搜索关键字。
调用 msgFtsInServer(options)
方法进行搜素,其中option
的keyword
参数表示搜索关键字。
Windows下如何清空历史记录?
在 Windows 中,存在 revert_by_online_query
参数,在清空历史消息时,可以配置是否可以通过服务端查询消息记录(含入库选项)进行恢复。true
:是;false
:否。
- 调用
BatchStatusDeleteAsync
方法清空历史消息,该接口在 SDK 内部逻辑中revert_by_online_query
参数默认为false
,即不会入数据库,重新获取本地也就查询不到历史消息。 - 调用
BatchStatusDeleteAsyncEx
方法清空历史消息,在清空事可以配置revert_by_online_query
参数,设置为 true,那么清空后,重新拉取云端历史消息(QueryMsgOnlineAsync
)会保存在本地数据库,后续可调用QueryMsgAsync
从本地拉取消息。
如何实现红包/礼物消息?
云信没有封装红包/礼物消息,需要开发者自行实现,而且这类涉及余额的内容需要和开发者应用服务器进行交互,建议的处理方式如下。
由于礼物消息和红包类似,下面以红包为例,介绍怎么通过云信IM来实现类似微信红包的效果。
- A给B发送红包时,发送自定义消息给B。
- B点击该自定义红包消息时,首先和开发者应用服务器交互,应用服务器判断该红包是否已领取,如果没有领取过则计算变更双方余额,并且维护领取记录以便后续查询。
B领取后,本地插入一条提示消息,显示“你领取了A的红包”。
同时修改原自定义红包消息的本地扩展字段,标记已领取并更新UI。
给A发送一条自定义系统通知,告知已领取(可以由B发送,也可以由应用服务器发送)。 - A根据收到的自定义系统通知,本地插入一条提示消息,显示“B领取了你的红包”。
点击原自定义红包消息时,更新其本地扩展字段,标记已领取并更新UI。
拼手气红包和普通红包原理是类似的,只是重复进行了领取和通知环节。对于漫游消息,开发者可以按需处理显示效果。
iOS 端发送视频消息,接收方在 Web 端 Chrome 浏览器上打开黑屏
问题原因
iOS 自带相机拍摄的视频默认编码方式是 H265 格式,而 Chrome 浏览器的 video 标签只能支持解码 H264,无法解码 H265,所以播放 iOS 自带相机拍摄的视频播放会黑屏;实际测试云信 iOS 端 IM Demo 中直接拍摄视频发给 Web 端 Chrome 浏览器可以正常播放,但是选择系统相机拍摄的视频发给 Web 端 Chrome 浏览器就会播放黑屏。
注意:新版本 Chrome 支持了 H265 硬解,实际测试 Mac Chrome 有能力硬解码 H265,而 Windows 大部分设备不支持。(Chrome 只支持 H265 硬解,不支持软解,更多详情请参考:https://www.zhihu.com/question/556331019/answer/2694657595)。
解决方案
需要引入#import "AVAsset+NIMKit.h"头文件, 然后 iOS 端需要修改 NIMKit 源码如下:
源码
- (void)requestAsset:(PHAsset *)asset handler:(void(^)(NSString *,PHAssetMediaType)) handler
{
__weak typeof(self) weakSelf = self;
if (asset.mediaType == PHAssetMediaTypeVideo) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionCurrent;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
[PHImageManager.defaultManager requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable assetR, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
NSError *error = nil;
NSString *outputPath = nil;
if ([[info objectForKey:PHImageResultIsInCloudKey] boolValue]) {
outputPath = nil;
error = [NSError errorWithDomain:@"nimdemo.netease.fetcher" code:0x1000 userInfo:@{NSLocalizedDescriptionKey:@"图片在iCloud上"}];
[weakSelf showErrorMsg:@"文件在iCloud上,无法发送"];
} else {
AVURLAsset *URLAsset = (AVURLAsset *)assetR;
NSString *outputFileName = [NIMKitFileLocationHelper genFilenameWithExt:@"mp4"];
outputPath = [NIMKitFileLocationHelper filepathForVideo:outputFileName];
//------修改部分
AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:URLAsset
presetName:AVAssetExportPresetMediumQuality];
session.outputURL = [NSURL fileURLWithPath:outputPath];
session.outputFileType = AVFileTypeMPEG4; // 支持安卓某些机器的视频播放
session.shouldOptimizeForNetworkUse = YES;
session.videoComposition = [URLAsset nim_videoComposition]; //修正某些播放器不识别视频Rotation的问题
[session exportAsynchronouslyWithCompletionHandler:^(void)
{
dispatch_async(dispatch_get_main_queue(), ^{
if (session.status == AVAssetExportSessionStatusCompleted)
{
BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:outputPath];
if (!fileExist) {
[weakSelf showErrorMsg:@"图片在本地不存在,无法发送"];
} else {
//NSError *error;
//[NSFileManager.defaultManager copyItemAtURL:URLAsset.URL toURL:[NSURL fileURLWithPath:outputPath] error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
handler(!error ? outputPath : nil, PHAssetMediaTypeVideo);
});
}
}
});
}];
//-------修改部分
//----注释部分
// BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:URLAsset.URL.path];
// if (!fileExist) {
// error = [NSError errorWithDomain:@"nimdemo.netease.fetcher" code:0x1001 userInfo:@{NSLocalizedDescriptionKey:@"图片在本地不存在"}];
// [weakSelf showErrorMsg:@"图片在本地不存在,无法发送"];
// } else {
// [NSFileManager.defaultManager copyItemAtURL:URLAsset.URL toURL:[NSURL fileURLWithPath:outputPath] error:&error];
// }
}
// dispatch_async(dispatch_get_main_queue(), ^{
// handler(!error ? outputPath : nil, PHAssetMediaTypeVideo);
// });
//----注释部分
}];
});
}
if (asset.mediaType == PHAssetMediaTypeImage)
{
[asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
NSString *path = contentEditingInput.fullSizeImageURL.relativePath;
handler(path,contentEditingInput.mediaType);
}];
}
}
从相册获取到视频文件之后通过修改 AVURLAsset 来修改视频格式 或者参照以上代码示例自己实现从相册选择视频文件的逻辑来修改视频格式。该代码还能解决从相册选择视频发送,视频封面被旋转了90度的问题。