IM 即时通讯
Android
开发指南

消息相关

更新时间: 2024/01/03 10:31:26

本文介绍 NIM SDK 消息和会话相关的问题及其解决方法。

如何接收 IM 消息?

Android
  • 点对点消息和群聊消息都通过监听MsgServiceObserve.observeReceiveMessage 回调获取,两者通过会话类型进行区分 SessionTypeEnum。 通过NIMClient.getService(MsgServiceObserve.class).observeReceiveMessage(incomingMessageObserver, true)注册消息接收观察者,新消息到达时会收到通知,其中包含 IMMessage的列表。
  • 聊天室消息通过监听 ChatRoomServiceObserver.observeReceiveMessage回调获取。 通过NIMClient.getService(ChatRoomServiceObserver.class).observeReceiveMessage(Observer> observer, true)注册聊天室消息接察者,新消息到达时会收到通知,其中包含ChatRoomMessage的列表。
iOS

点对点消息、群聊消息以及聊天室消息通过同一个回调 NIMChatManagerDelegateonRecvMessages: 回调获取,三者通过会话类型进行区分 NIMSessionType

通过 - (void)onRecvMessages:(NSArray *)messages 注册消息接收观察者,新消息到达时会收到通知,其中包含 NIMMessage的列表。

Windows/macOS
  • 点对点消息和群聊消息都通过通过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和消息内容。
Web
  • 点对点消息和群聊消息都通过初始化 SDK 时NIM.getInstanceonmsg注册的回调函数进行通知,其中包含消息对象。两者通过会话类型进行区分 scene
  • 聊天室消息通过初始化聊天室时Chatroom.getInstanceonmsgs注册的回调函数onChatroomMsgs进行通知,其中包含聊天室消息数组。

如何设置和获取 IM 消息的扩展字段?

单聊或群聊消息具有服务端扩展字段和客户端扩展字段。聊天室消息没有客户端扩展字段。

服务端扩展字段只能在消息发送前设置,会同步到其他端;客户端扩展字段在消息发送前后设置均可,不会同步到其他端。

  • 扩展字段,请使用JSON格式封装,并传入非格式化的JSON字符串,最大长度1024字节。
  • 设置消息的本地扩展字段之后,必须调用相应的更新方法,否则无法生效。
Server

调用以下接口时,参数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:发送聊天室消息
Android
  • 设置消息的扩展字段:

    • 单聊和群聊消息:构造IMMessage对象时,通过setRemoteExtension方法设置消息的服务端扩展字段,通过setLocalExtension方法设置本地客户端扩展字段。
    • 聊天室消息:ChatRoomMessage继承自IMMessage,通过setRemoteExtension方法设置聊天室消息的服务端扩展字段。
  • 更新消息的扩展字段: 通过NIMClient.getService(MsgService.class).updateIMMessage(IMMessage message)方法更新消息的本地扩展字段。

  • 获取消息的扩展字段:

    • 单聊和群聊消息:通过IMMessage对象的getRemoteExtension()方法获取消息的服务端扩展字段,通过getLocalExtension()方法获取消息的本地扩展字段。
    • 聊天室消息:通过ChatRoomMessage对象的getRemoteExtension()方法获取聊天室消息的服务端扩展字段。
iOS
  • 设置消息的扩展字段:发送消息时,构造的 NIMMessage对象中,通过remoteExt属性设置消息的服务端扩展字段,通过localExt属性设置消息的本地客户端扩展字段(聊天室消息的本地客户端扩展字段不生效)。

  • 更新消息的扩展字段:通过[[NIMSDK sharedSDK].conversationManager updateMessage:(NIMMessage *)message forSession:(NIMSession *)session completion:(nullable NIMUpdateMessageBlock)completion]方法更新消息的本地扩展字段。

  • 获取消息的扩展字段:通过NIMMessage对象的remoteExt属性获取消息的服务端扩展字段,通过localExt属性获取消息的本地扩展字段。

Windows/macOS
  • 设置消息的扩展字段:

    • 单聊和群聊消息:调用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"表示聊天室消息的服务器扩展字段。
Web
  • 设置消息的扩展字段:
    • 单聊和群聊消息:发送消息时,构造的消息对象中,custom 字段表示消息的服务端扩展字段。 注意:目前设置localCustom本地客户端扩展字段不生效。
    • 聊天室消息:发送聊天室消息时,构造的聊天室消息对象中,custom字段表示聊天室消息的服务端扩展字段。
  • 更新消息的扩展字段: 通过如下方法更新消息的本地扩展字段。
    nim.updateLocalMsg({
        idClient: msg.idClient, localCustom: '{"key","value"}',
        done: updateLocalMsgDone
    })
    
  • 获取消息的扩展字段:
    • 单聊和群聊消息:通过IMMessage对象的custom字段获取消息的服务端扩展字段,通过localCustom字段获取消息的本地扩展字段。
    • 聊天室消息:通过ChatroomMessage对象的custom字段获取聊天室消息的服务端扩展字段。

如何处理消息丢失(收不到)的问题?

单聊消息和群聊消息丢失

  1. 请先确定是历史消息没有收到,还是实时消息没有收到。
    • 历史消息包括离线消息和漫游消息,这两种消息存在数量限制,请确认是否超限,具体请查看:消息概述
    • 如果是实时消息没有收到,请继续查看下文。
  2. 检查是否存在多端登录的情况,如果多端登录的情况下,消息配置了不需要多端同步,则只有一端可以收到消息。
  3. 检查发送方和接收方账号是否登录成功。
  4. 检查接收方是否处于非登录状态(例如掉线、自动重连失败、被踢等)。
  5. 检查消息是否正确设置接收方。
  6. 检查发送方是否发送成功。
  7. 检查接收方是否触发消息通知。
  8. 对于点对点聊天,如果配置了非好友不允许发消息,客户端 SDK 向非好友发消息会返回 403。
  9. 对于点对点聊天,如果被拉黑之后继续发送消息,对方是无法接收的。
    • 拉黑是单向的。当A被B拉黑后,A不能给B发消息(但是B还能给A发)。
    • 当A被B拉黑后,A给B发消息将返回错误码 7101。如果选择重发,为了避免开发者循环重试以及方便上层做UI展示,SDK会返回状态码200,但是这条消息实际上仍然不会发给A。但是该消息带有漫游属性,A其他设备能漫游下发。
    • 如果配置了非好友不允许发消息,被拉黑后发消息将返回403(不再返回7101)。
  10. 如果是群聊,检查接收方是否为群成员。
  11. 检查是否命中了反垃圾。关于IM反垃圾,请查看内容审核
  12. 检查是否配置了第三方回调,拒绝了消息投递。关于第三方回调,请查看回调说明

聊天室消息丢失

  1. 检查发送方和接收方账号是否登录成功。
  2. 检查聊天室消息是否正确设置接收方。
  3. 检查聊天室消息发送方是否发送成功。
  4. 检查接收方是否在聊天室内。
  5. 检查接收方是否触发消息通知。
  6. 检查是否命中了反垃圾。关于IM反垃圾,请查看内容审核
  7. 匿名进入聊天室,只能收消息不能发消息,发消息会返回403,聊天室其他成员是收不到的。

如何查看消息设置的接收方?

单聊和群聊消息:

Android

调用 MessageBuilder 的各种创建消息的方法时,参数sessionId表示聊天对象ID(消息接收方)。

如果是点对点消息,sessionId 就是对方 accid ;如果是群聊,sessionId 就是群组ID teamId

iOS

调用接口[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error]时,session表示消息接收方。

如果是点对点消息,session 就是对方 accid ;如果是群聊,session 就是群组ID teamId

Windows/macOS

调用接口 nim_talk_send_msg时,json_msg参数的消息结构Json Keys中,kNIMMsgKeyToAccount表示消息接收方。

如果是点对点消息,kNIMMsgKeyToAccount 就是对方 accid ;如果是群聊,kNIMMsgKeyToAccount就是群组ID kNIMTeamInfoKeyID

Web

通过nim的各种send方法创建的消息对象中,to字段表示消息接收方。

如果是点对点消息,to 就是对方 accid ;如果是群聊,to 就是群组ID teamId

聊天室消息:

Android

调用接口NIMClient.getService(ChatRoomService.class).sendMessage(ChatRoomMessage msg, boolean resend)发送聊天室消息时,确保通过ChatRoomMessageBuilder接口创建的各种消息对象中,包含的roomId参数配置正确。

iOS

调用接口[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error]发送聊天室消息时,确保其中构造的会话对象NIMSession的会话类型sessionTypeNIMSessionTypeChatroomsessionId配置为正确的聊天室roomId

Windows/macOS

调用接口nim_chatroom_send_msg (const int64_t room_id, const char *msg, const char *json_extension)发送聊天室消息时,确保room_id参数配置正确。

Web

通过chatroom的各种send方法发送聊天室消息时,可以通过chatroom.getChatroom(options)方法获取聊天室信息,通过获取成功的回调函数来获取聊天室id。

如何判断 IM 消息发送成功?

单聊和群聊消息:

Android

调用消息发送接口时,设置回调函数:NIMClient.getService(MsgService.class).sendMessage(IMMessage msg, boolean resend).setCallback(callback),判断是否进入onSuccess回调。

iOS

调用消息发送接口[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error]时,判断error == nil表示方法调用成功;实现NIMChatManagerDelegate–sendMessage:didCompleteWithError:方法,判断error == nil表示消息已经发送至服务器。

Windows/macOS

发送消息状态通过登录前注册的全局回调nim_talk_reg_ack_cb获取,判断result中“发送消息回执Json Keys”的kNIMSendAckKeyMsgId对应的kNIMSendAckKeyRescode是否为kNIMResSuccess = 200

Web

在消息发送的done回调中根据消息对象的idClient字段和error对象来确定消息的发送状态,error为空表示消息发送成功。

聊天室消息:

Android

调用接口NIMClient.getService(ChatRoomService.class).sendMessage(ChatRoomMessage msg, boolean resend).setCallback(new RequestCallback<Void>() {})发送聊天室时,如果回调onSuccess表示发送成功。

iOS

调用接口[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error]发送聊天室消息时,判断error == nil表示方法调用成功;实现NIMChatManagerDelegate–sendMessage:didCompleteWithError:方法,判断error == nil表示消息已经发送至服务器。

Windows/macOS

调用接口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表示发送成功。

Web

通过chatroom的各种send方法发送聊天室消息时,可以通过设置的回调函数done: sendChatroomMsgDoneerror参数获取发送结果,如果!error没有错误表示发送成功。

如何在单聊/群聊消息发送成功后获取消息的内容?

当 NIM SDK 发送消息成功后后,若需要获取发送的消息内容,可以通过以下方法实现。

Android
  1. 通过接口NIMClient.getService(MsgServiceObserve.class).observeMsgStatus(Observer<IMMessage> observer, true)注册消息状态变化观察者,该接口可以监听消息发送状态变化中回调IMMessage对象。
  2. 通过IMMessage对象的getDirect()方法获取消息方向,通过getSessionId()方法获取聊天对象的accid/群组id,通过getContent()方法获取文本消息具体内容,通过getAttachment()方法获取文件消息附件对象,通过getStatus()方法获取消息收发状态,通过getTime()方法获取消息时间(单位为毫秒)。
iOS
  1. 实现NIMChatManagerDelegate-sendMessage:didCompleteWithError:方法来接收发送消息完成回调,其中回调NIMMessage对象。可以通过isOutgoingMsg属性判断是否是发送出去的消息。
  2. 通过session属性获取聊天对象的accid/群组id/聊天室id,通过text属性获取消息文本(仅适用于文本消息和提示消息),通过messageObject属性获取消息附件内容,通过deliveryState属性获取发送消息的投递状态,通过timestamp属性获取消息发送时间。
Windows/macOS
  1. 通过全局回调nim_talk_reg_ack_cb注册发送消息结果回调函数nim_talk_ack_cb_func,回调result为发送消息回执Json Keys,其中kNIMSendAckKeyMsgId = "msg_id"表示客户端消息id。
  2. 通过nim_msglog_query_msg_by_id_async根据消息id查询本地单条消息,回调函数nim_msglog_query_single_cb_funcresult为消息结构Json Keys。 其中,kNIMMsgKeyToAccount = "to_accid"表示消息接收方accid/群组id,kNIMMsgKeyBody = "msg_body"表示消息正文,kNIMMsgKeyAttach = "msg_attach"表示消息附件kNIMMsgKeyTime = "time"表示消息时间戳,kNIMMsgKeyLocalLogStatus = "log_status"表示消息状态NIMMsgLogStatus
Web
  1. 在发送消息接口的done回调中返回IMMessage对象。
  2. 通过flow属性获取消息方向,通过to属性获取聊天对象的accid/群组id,通过text属性获取文本消息的内容,通过file属性获取文件消息的文件对象,通过status属性获取消息发送状态,通过time属性获取消息时间戳。

如何对自定义消息进行安全通检测?

自定义消息比较特殊,开发者如需自定义消息经由安全通反垃圾,在发送消息时需要通过上述相关字段进行配置,说明自定义消息的内容是文本还是图片、视频等,以便安全通按照相应类型进行反垃圾处理。开发配置中,如果配置了不经过安全通反垃圾,则消息会经过通用反垃圾。

Server

通过useYidun来选择该条消息是否经过安全通,通过配置antispam, antispamCustom来设置自定义消息的反垃圾类型。具体请参考发送消息中的具体参数。

Android

调用 NIMMessagesetNIMAntiSpamOption 方法,传递参数为 NIMAntiSpamOption 对象,开发者需要构造这个 NIMAntiSpamOption 对象。其中,content 参数用于说明自定义消息的内容是文本还是图片,以便安全通按照相应类型进行内容安全检测处理。具体请参考安全通文档

iOS

需要对即将发送的消息 NIMMessageantiSpamOption 参数传递 NIMAntiSpamOption 对象,开发者需要构造这个 NIMAntiSpamOption 对象。其中,content 参数用于说明自定义消息的内容是文本还是图片,以便安全通按照相应类型进行内容安全检测处理。具体请参考安全通文档

Windows/macOS

需要对即将发送的消息的 kNIMMsgKeyAntiSpamEnable, kNIMMsgKeyAntiSpamContent, kNIMMsgKeyAntiSpamUsingYiDun 参数进行配置,具体请参考安全通文档

Web

需要对即将发送的消息的 yidunEnable, antiSpamContent, antiSpamUsingYidun 参数进行配置。具体请参考消息配置选项

如果开通安全通,消息抄送会携带yidunRes字段,并通过其中action字段标记检测结果,0:通过,1:嫌疑,2:不通过 (只有yidunBusType为0或2时,抄送时才有此字段)。

yidunBusType 枚举:0:安全通文本反垃圾业务;1:安全通图片反垃圾业务;2:用户资料反垃圾业务;3:用户头像反垃圾业务。

对应的图片返回labels字段, 文本类反垃圾参考labels字段的释义、 图片类反垃圾参考labels字段的释义

如何获取最近会话列表?

获取最近会话只查询客户端本地信息。服务端接口无法获取最近会话。

最近会话不会漫游,但是其中的消息可以漫游。在另一台设备上接收漫游消息后,也会产生最近会话。

服务器会下发最近1个月内最多100个会话的已读回执,客户端本地会增量更新。

Android

调用接口NIMClient.getService(MsgService.class).queryRecentContacts().setCallback(new RequestCallbackWrapper>() {...}时,在onResult中回调最近会话列表(最近联系人列表)。

当有消息收发时,SDK 会通知最近会话更新,需要通过接口NIMClient.getService(MsgServiceObserve.class).observeRecentContact(Observer<java.util.List<RecentContact>> observer, true);注册最近会话列表变化观察者,onEvent中会回调消息列表。

iOS

调用接口 NSArray *recentSessions = [NIMSDK sharedSDK].conversationManager.allRecentSessions 获取最近会话列表。

提前监听NIMConversationManagerDelegate中的相应回调方法,当增加、修改、删除最近会话时,SDK 提供对应的回调通知。

  • 增加最近会话的回调:-didAddRecentSession:totalUnreadCount:

  • 修改最近会话的回调:-didUpdateRecentSession:totalUnreadCount:

  • 删除最近会话的回调:-didRemoveRecentSession:totalUnreadCount:

  • 最近会话全部加载完成的回调:NIMConversationManagerDelegate-didLoadAllRecentSessionCompletion

    必须同时满足以下条件,该回调才会执行。

    • SDK 版本为7.0.0及以上。
    • 需要设置NIMSDKConfigasyncLoadRecentSessionEnabled属性为YES
    • 本地会话很多,处理耗时较长的情况下:SDK正在查询会话 > 开发者调用allRecentSessions > 此时SDK尚未返回全部会话 > SDK查询完全部会话 > SDK回调-didLoadAllRecentSessionCompletion > 开发者再次调用allRecentSessions获取全量会话并更新UI。
    • 本地会话较少的情况下,当开发者调用allRecentSessions时,如果SDK已经返回全部会话,则不会触发该回调。
Windows/macOS

调用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”。

Web

根据浏览器是否支持数据库、开发者是否开启数据库的不同,获取到的最近会话不同。

  • 浏览器支持数据库时, SDK 会将会话存储于本地数据库中,并且在初始化NIM.getInstance时通过回调onsessions将最多 100 条会话列表返回给开发者,可以调用接口nim.getLocalSessions(options)获取更多最近会话。

    注意:第一次调用nim.getLocalSessions时,不要传入lastSessionId字段。

  • 浏览器不支持数据库,或者初始化时配置了禁用数据库,则 SDK 无法在本地维护数据库,最近会话只能依赖消息产生。初始化NIM.getInstance时,配置会话更新回调函数onupdatesession来接收更新会话的回调,包括发送和接收消息、设置当前会话,重置会话未读数等。

如何实现未读数清零

  • 如果当前正在聊天的会话,不需要显示未读数,必须清零。
  • 如果非当前会话,只有当用户希望主动对某个会话或者全部会话进行清零的时候,才调用接口进行清零。
Android
  • 调用接口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)通知。
iOS
  • 调用接口[[NIMSDK sharedSDK].conversationManager markAllMessagesReadInSession:(NIMSession *)session]将一个会话中的所有消息标记为已读。如果是当前会话,每次收到消息都需要调用该接口。
  • 调用接口-markAllMessagesRead设置所有会话消息为已读(所有联系人的未读数清零)。
Windows/macOS
  • 调用接口nim_session_set_unread_count_zero_async设置所有会话消息为已读(所有联系人的未读数清零)。如果是当前会话,每次收到消息都需要调用该接口。
  • 调用接口nim_msglog_batch_status_read_async来批量设置未读消息为已读状态。
Web
  • 调用接口nim.setCurrSession('sessionId')设置当前会话。将此会话未读数置为0,并且此会话收到消息不再更新未读数。
    Web SDK的sessionId,根据会话类型的不同,包含不同的前缀(p2p/team),例如:p2p-accidteam-teamid
  • 调用接口nim.resetCurrSession()来重置当前会话。重置当前会话后, 所有会话在收到消息之后正常更新未读数。
    此接口和nim.setCurrSession组合起来使用,相当于一对开关。nim.setCurrSession将会话未读数置为0并且不再更新未读数,而nim.resetCurrSession()用来恢复更新未读数。
  • 调用接口nim.resetSessionUnread('sessionId')重置会话未读数。将此会话未读数置为0,之后收到消息重新计算未读数。
  • 调用接口nim.resetAllSessionUnread()来重置内存中的所有会话未读数。之后收到消息重新计算未读数。

9.1.0 及以下版本下,如果开发者在同一浏览器、不同标签页、(开启多端登录)同时登录同一账号,那么请在初始化SDK时配置db: false,以免多标签页共用同一数据库时造成未读数异常等情况。

:::

如何设置和获取最近会话的扩展字段?

  • 最近会话是客户端本地概念,因此只支持本地扩展字段,没有服务端扩展字段,也不会同步到其他客户端。
  • 最近会话的扩展字段请使用JSON格式封装,并传入非格式化的JSON字符串。扩展字段可以用于实现@提醒等功能。

设置最近会话的扩展字段:

Android

通过最近会话对象 RecentContactsetExtension(Map extension)方法设置最近会话的扩展字段。

注意:如果希望持久化该字段(保存到数据库中),请调用NIMClient.getService(MsgService.class).updateRecent(RecentContact recent)或者updateRecentAndNotify(RecentContact recent)更新会话属性(写入数据库)。这两个方法的区别在于后者会触发会话更新的通知。

iOS

调用接口-(void)updateRecentLocalExt:(nullable NSDictionary *)ext recentSession:(NIMRecentSession *)recentSession设置最近会话的本地扩展,其中参数ext表示扩展字段,开发者需要确保 NSDictionary 可以转换为JSON。

Windows/macOS

通过接口nim_session_set_extend_data设置会话项扩展数据,其中参数data表示最近会话的扩展字段。

Web

通过接口nim.updateLocalSession({id:..., localCustom: '{"key", "value"}', done:...})更新本地会话时,其中的localCustom字段表示最近会话的扩展字段。

注意: 如果浏览器不支持数据库,或者对应的会话不存在,SDK都算更新成功。

获取最近会话的扩展字段:

Android

通过RecentContact最近会话对象的getExtension()方法获取最近会话的扩展字段。

iOS

通过NIMRecentSession最近会话对象的localExt属性获取最近会话的扩展字段。

Windows/macOS
  • 通过接口nim_session_query_all_recent_session_async查询会话列表时,回调函数nim_session_query_recent_session_cb_funcresult为会话列表的Json Keys。
  • 注册nim_session_reg_change_cb最近会话列表项变更通知全局回调,回调函数nim_session_change_cb_funcresult为会话列表的Json Keys。

以上两个回调函数的会话列表Json Keys中,kNIMSessionExtendedData = "extend_data"表示最近会话的扩展字段。

Web

通过Session对象的localCustom属性获取最近会话的扩展字段。

收到消息时,如何获取用户资料?

  • 接收到单聊/群聊消息时,只能通过消息体本身获取到发送者的账号和昵称,其他资料需要通过用户账号(accid)再查询。

  • 接收到聊天室消息时,只能通过消息体本身获取到发送者的账号,通过聊天室消息扩展获取到发送者的昵称和头像地址,其他资料需要通过用户账号(accid)再查询。

Android

收到消息时,会得到IMMessage列表,可以通过NIMMessagegetFromAccount()getFromNick方法获取发送者的 accid 和昵称,再通过getFromNick()方法获取发送方的昵称。

iOS

收到消息时,会得到 NIMMessage列表,可以通过NIMMessage对象的from字段获取发送者的 accid,通过 senderName字段获取发送者的昵称(注意是发送者当前的昵称而不是发送消息时的昵称)。

Windows/macOS

通过接收消息的回调函数 nim_talk_receive_cb_func获取消息内容content,消息结构 Json Keys 中,kNIMMsgKeyFromAccount表示发送者的 accid,kNIMMsgKeyFromNick表示发送者的昵称。

Web

收到消息时,在onmsg回调函数中得到IMMessage消息对象,其中from参数表示发送者的accid,fromNick表示发送者的昵称。

如何对文本消息进行关键字搜索?

IM SDK 支持对客户端本地数据库中保存的文本消息进行关键字搜索。

Android

通过 NIMClient.getService(MsgService.class).searchMessageHistory(String keyword, List<String> fromAccounts, IMMessage anchor, int limit).setCallback(new RequestCallbackWrapper<List<IMMessage>>(){ ... }) 方法进行搜素,其中参数keyword表示搜索关键字。

支持基于 Lucene 的全文检索插件,具体请参见开发文档

iOS

通过NIMConversationManager中的–searchMessages:option:result:方法或–searchAllMessages:result:方法进行搜索,在参数option(NIMMessageSearchOption)中指定searchContent进行关键字搜索。

Windows/macOS
  • C: 通过nim_msglog_query_msg_by_options_async方法进行搜索,通过参数search_content指定搜索关键字。
  • C++:通过 nim::MsgLog::QueryMsgByOptionsAsync 方法进行搜索,通过参数search_content指定搜索关键字。
  • C#:通过NIM.Messagelog.MessagelogAPI.QueryMsglogByCustomCondition方法进行搜索,通过参数searchContent指定搜索关键字。
Web

在开启数据库并且浏览器支持数据库的情况下,可以通过nim.getLocalMsgs方法进行搜索,传入参数keyword进行关键字搜索。

如何开启未读数多端同步?

开启会话的未读数多端同步后,在一个端已读的会话在其它端也会被标记为已读。

客户端批量处理会话未读数,一次最多 10 条。如果要清理未读数的会话较多,又希望正常同步,可以分开多次批量清理,每次不超过 10 条。

Android

默认关闭未读数多端同步。

可以通过设置SDKOptions对象的sessionReadAck属性为 true 实现。

iOS

默认关闭未读数多端同步。

可以通过设置[NIMSDKConfig sharedConfig].shouldSyncUnreadCount = YES 实现。

Windows/macOS

默认开启未读数多端同步。

可以通过调用 nim_client_init 初始化,在初始化的json_extension扩展参数中,配置kNIMSyncSessionAck为 true 实现。

Web

默认关闭未读数多端同步。

可以通过调用NIM.getInstance初始化,在初始化参数中配置syncSessionUnread: true实现。

如何在本地插入一条消息?

IM SDK 支持插入消息到本地数据库,但不发送到服务端,主要使用于在App中保存本地提醒类消息。

自 7.0.0 版本起,IM SDK 支持插入本地消息时设置发送方,以便本地展示(例如客服咨询场景中自动发送欢迎语),但实际上对方并未发送此消息。

通过以下方法往本地消息历史数据库里写入一条消息(如果已存在这条消息,则更新该条消息)。

Android
  • 调用 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)通知。。
iOS

通过NIMConversationManager协议的 –saveMessage:forSession:completion: 方法来写入本地消息,方法为异步写入,无须开发者在上层单独开线程,直接在当前线程调用即可。

构造NIMMessage消息对象时,可以通过from字段设置消息发送者的accid,通过timestamp字段设置保存消息的时间。

注意:不允许插入已存在的消息。当保存消息成功之后,会收到NIMChatManagerDelegate中的onRecvMessages:回调。

Window/macOS
  • C: 通过nim_msglog.h中定义的nim_msglog_insert_msglog_async方法插入本地消息。其中参数need_update_session表示是否更新会话列表(一般最新一条消息会有更新的需求)。参数json_msg中的kNIMMsgKeyFromAccount = "from_id"表示消息发送方accid,kNIMMsgKeyTime = "time"表示消息时间戳。
  • C++: 通过nim::MsgLog::WriteMsglogToLocalAsync方法插入本地消息,通过msgsender_accid_属性指定发送者accid,通过timetag_属性指定消息时间戳。示例代码请点此查看
Web

发送消息时可以指定参数isLocaltrue,那么 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

如何获取消息附件上传下载进度?

Android

通过NIMClient.getService(MsgServiceObserve.class).observeAttachmentProgress(Observer observer, true)来注册消息附件上传/下载进度观察者。通过AttachmentProgressgetTotal()方法获取文件总长度,getTransferred()获取已经传输的字节数,getUuid()方法获取附件对应的消息的uuid。

通过uuid查询对应消息的方法为:

  • 异步版本:NIMClient.getService(MsgService.class).queryMessageListByUuid(uuids)
  • 同步版本:NIMClient.getService(MsgService.class).queryMessageListByUuidBlock(uuids)
iOS
  • 上传进度: 通过NIMChatManagerDelegate-sendMessage:progress:方法的progress参数获取附件上传进度。
  • 下载进度: 通过NIMChatManagerDelegate-fetchMessageAttachment:progress:方法的progress参数获取附件下载进度。
Windows/macOS
  • 上传进度: 调用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表示文件大小。
Web
  • 上传进度: 调用nim.sendFile发送文件消息时,通过uploadprogress参数配置上传进度回调函数,例如uploadprogress: function(obj)。其中回调对象objtotal参数表示文件总大小字节数,loaded参数表示已上传的字节数,percentage参数表示上传进度(格式为数字,例如80),percentageText参数表示上传进度文本(格式为百分比,例如80%)。
  • 下载进度: 无。

如何获取会话未读数?

Android
  • 调用 NIMClient.getService(MsgService.class).getTotalUnreadCount() 方法获取总未读数。
  • 调用 RecentContactgetUnreadCount()方法获取指定会话的未读数。
iOS
  • 调用 NIMConversationManagerallUnreadCount方法获取总未读数。
  • 调用 NIMRecentSessionunreadCount属性获取指定会话的未读数。
Windows/macOS

通过全局注册nim_session_reg_change_cb来监听最近会话变更。回调函数nim_session_change_cb_func中,total_unread_counts参数表示总未读数,result参数表示[会话列表的Json Keys],其中kNIMSessionUnreadCount = "unread_count"参数表示对应于会话kNIMSessionId = "id"的未读数。

Web

只能在主线程操作获取未读数。

  • 会话(Session)对象的unread值为会话的未读数。
  • 总未读数没有直接获取的接口,需要遍历各会话未读数进行累加。

如何设置缩略图动图?

IM SDK 发送动态图片时,服务器生成的默认缩略图为获取动态图片第一帧作为静态图片

若要服务器生成缩略图动图,需要在客户端设置支持对缩略动图。

Android

调用NIMClient.initNIMClient.initSDK 初始化时,配置初始化参数中的SDKOptions对象的animatedImageThumbnailEnabled属性为true

iOS

调用[[NIMSDK sharedSDK] registerWithOption:option]初始化 SDK 之前,配置[NIMSDKConfig sharedConfig].animatedImageThumbnailEnabled = YES

发送图片消息时,NIMImageObject–initWithImage:方法只支持 JPEG 和 PNG 两种格式,动图(GIF、WebP)需要通过–initWithFilepath:–initWithData:extension:方法进行初始化。

Windows/macOS
  • 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初始化时,在初始化参数configCommonSetting中,AnimatedImageEnabled表示对缩略图动图的支持,将其设置为true
Web

暂不支持生成缩略图动图。

如何标记本地消息为已读?

某些应用场景下,需要在本地将消息标记为已读,用于显示不同的业务状态。

例如,对于收到的语音消息,未读时默认显示一个小红点提示,如果已读则将消息标记为本地已读来取消小红点提示。

Android

调用 IMMessagesetStatus(MsgStatusEnum.read)方法。对于发送的消息,表示对方已读;对于接收的消息,表示自己已读,一般仅用于音频消息。

目前read只是用在了语音消息上,自己播放了就会变为read。对方播放后,本端是无感知的。

注意:此接口只是临时修改内存中的消息状态,如果需要持久化该状态到数据库中,还需要调用NIMClient.getService(MsgService.class).updateIMMessageStatus(message)方法。

iOS

设置NIMMessageisPlayed属性为YES表示消息被播放过(对于聊天室消息无效)。

Windows/macOS
  • 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

由于 Web 浏览器不一定支持本地数据库,因此可能导致状态丢失从而表现不符合预期,所以SDK不提供相关接口。如果开发者确实需要在 Web 端实现,建议从应用层自行处理。

如何过滤消息?

IM SDK 提供消息过滤忽略的功能。消息过滤后,SDK 将不存储对应的消息,也不会上抛给接收回调,因此应用层不会收到对应的消息。

  • 下文提到的通知消息属于一种特殊的消息,而不是系统通知。通知消息和系统通知的区别,请参见开发文档
  • 消息过滤对在线消息、离线消息、漫游消息等有效。查询本地和云端历史记录目前只有移动端是支持的,PC和Web端无法过滤。
  • 不要在消息过滤函数或方法中进行耗时操作,否则将导致线程阻塞。
  • 目前不支持过滤聊天室消息。
Android

建议在Application的onCreate中,SDK初始化之后,注册过滤器。

调用 NIMClient.getService(MsgService.class).registerIMMessageFilter(new IMMessageFilter() { public boolean shouldIgnore(IMMessage message) {...}}) 方法注册过滤器,返回true表示过滤。

iOS

在初始化 SDK 时,创建一个NIMSDKConfigDelegate对象,实现下列方法,并将此对象赋值给NIMSDKConfigdelegate属性。

  • -(BOOL)shouldIgnoreNotification:(NIMNotificationObject *)notification方法,返回YES表示忽略某条通知消息。
  • -(BOOL)shouldIgnoreMessage:(NIMMessage *)message方法,返回YES表示忽略某条普通消息。
Windows/macOS

目前 IM SDK PC端只支持过滤群通知消息,不支持过滤其他类型的消息。建议在 SDK 初始化之后,登录之前,注册过滤器。

通过nim_talk_reg_notification_filter_cb注册接收群通知消息是否需要过滤的全局回调,其中参数nim_talk_team_notification_filter_func表示群通知消息是否需要过滤的函数定义。

Web

在初始化SDK时,配置以下参数:

  • 配置shouldIgnoreNotification参数,表示是否需要忽略某条通知消息。该参数类型为函数(function),接收一个通知消息对象,如果该函数返回true,那么SDK将忽略此条通知消息。
  • 配置shouldIgnoreMsg参数,表示是否要忽略某条普通消息。该参数类型为函数(function),接收一个消息对象,如果该函数返回true,那么SDK将忽略此条消息。

如何删除历史消息和漫游消息?

调用下列接口成功后,服务器将清空此刻之前保存的对应聊天对象的历史消息或漫游消息(只影响自己不影响对方)。

  • 如果应用没有在云信控制台开启消息漫游,调用下列接口会返回403。
  • 对于点对点会话,还可以在清空会话历史的同时清空漫游消息。
  • SDK 支持从服务器上清空某一点对点会话的历史和漫游消息。清空后,自己不能再查询到清空时间点之前和对方的聊天消息。清空漫游消息后,服务器无法再下发清空时间点之前该会话的漫游消息。
  • 删除后只影响自己,不影响对方的历史和漫游消息。例如,A用户清空和B用户的会话记录之后,调用服务端 API 查询历史记录时,如果from设置为A的情况下,查询结果为空;但是from设置为B的情况下,可以正常查询到结果。
Server
  • 调用 https://api.netease.im/nimserver/msg/delRoamSession.action API 删除漫游消息。
  • 调用 https://api.netease.im/nimserver/msg/delMsg.action API 删除指定消息(会删除对应的离线消息、漫游消息和历史消息)。
Android
  • 调用 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)方法删除历史消息,可设置同时删除漫游消息或仅删除历史下消息。
iOS
  • 调用 NIMConversationManager-(void)deleteRemoteSessions:(NSArray *)sessions completion:(nullable NIMRemoveRemoteSessionBlock)completion方法删除指定会话的漫游消息。调用成功后,当前会话之前的消息都不会漫游到其他端。
  • 调用 NIMConversationManager–deleteSelfRemoteSession:option:completion: 方法删除云端历史消息。若不设置删除选项,服务端默认会同时删除漫游消息。
Windows/macOS

调用以下接口删除会话的云端历史消息和漫游消息。

  • C: nim_msglog_delete_history_online_async
  • C++: MsgLog::DeleteHistoryOnlineAsync
  • C#: NIM.Messagelog.MessagelogAPI.DeleteHistoryOnlineAsync
Web

调用 clearServerHistoryMsgsWithSync 方法删除指定会话的云端历史消息和漫游消息,其中,scene表示消息场景,包括:'p2p''team'

  • 当配置scene: 'p2p'时,to表示对方的accid。
  • 当配置scene: 'team'时,to表示群ID。

注意:调用成功后,本地数据库中指定会话的消息也会被删除,但是数据库中会话会保留,若需要删除数据库中的会话,可以调用 deleteLocalSession 删除。

如何查询指定消息类型的历史消息?

Android
  • 查询云端记录:
    • 调用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参数查询指定消息类型的历史消息。
iOS
  • 查询云端记录:调用NIMConversationManager–fetchMessageHistory:option:result:方法,通过设置(NIMHistoryMessageSearchOption *)optionmessageTypes参数查询指定消息类型的历史消息。从7.2.0版本开始,NIMHistoryMessageSearchOption中新增参数syncMessageTypes,表示是否将查询结果存入本地数据库。7.2.0(不含)之前的版本,查询结果不存本地数据库。
  • 查询本地记录:调用NIMConversationManager–searchMessages:option:result:–searchAllMessages:result:方法,通过设置(NIMMessageSearchOption *)optionmessageTypes参数查询指定消息类型的历史消息。
Windows/macOS
  • 查询云端记录:调用 MsgLog::QueryMsgOnlineAsync(const QueryMsgOnlineAsyncParam & param, const QueryMsgCallback & cb),通过设置QueryMsgOnlineAsyncParammsg_type_list_参数查询指定消息类型的历史消息。
  • 查询本地记录:调用MsgLog::QueryMsgByOptionsAsync,通过设置msg_type参数查询指定消息类型的历史消息。
Web
  • 查询云端记录:调用{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等。

在这些处理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**
  • 场景应用

    • 收到附件类消息(例如:图片、音频、视频、文件等),打开附件url进行下载时的默认文件名是该文件的随机编码值并且没有文件格式后缀,可以在url后拼接download字段来指定下载文件名,例如:https://nim-nosdn.netease.im/MTAxMTAwMg%3D%3D%2FbmltYV8yMDAzODU3NjRfMTU3NDY4NDkwNzI4Nl9hOTRkOWJjNy0xMzNiLTRlMDItYmZmNi1hY2Q4N2E0NzkyMTU%3D?**download=test.txt**
    • 开发者还可以在收到附件类消息时,获取附件的文件名和文件格式后缀,用来拼接到url中,即可实现下载时生成和发送时一致的文件名。当然,也可以按照自定义规则来生成指定的文件名。

如何单向删除消息?

自 7.4.0 版本开始,IM SDK 支持单向删除消息,仅支持删除一年内的消息。此功能需要联系技术支持开通

  • 用户单向删除一条消息成功后,该账号不能再从服务端拉取到该条消息。而且,登录此账号的其他端也会删除该消息的本地记录。除操作账号外,消息相关的其他账号不受该操作影响。
  • 单向删除消息后,会产生一个通知,通知也会漫游,漫游范围是10天150条单向删除通知。单向删除通知不属于漫游消息,不受消息漫游开关的影响。
  • 单向删除的消息中,漫游消息被删除的上限是150条。如果出现新设备会漫游到自己删除过的消息, 请检查是否删除的消息过多,删除的150条之后的消息还在漫游时间内,会在新设备上漫游到。
Android

调用 NIMClient.getService(MsgService.class).deleteMsgSelf(IMMessage msg, String ext).setCallback(...) 方法单向删除消息。

iOS

调用 deleteMessageFromServer:ext:completion:方法单向删除消息。

Windows/macOS

调用以下方法单向删除消息:

  • C:nim_msglog_delete_message_self_async
  • C++:MsgLog::DeleteMessageSelfAsync
Web

调用 deleteMsgSelf(options: { custom?: string; msg: NIMMessage; done?: any }) 方法单向删除消息。

如何接收快捷评论?

通过注册快捷评论事件监听接收快捷评论消息。

  • 快捷评论支持点对点和高级群,不支持聊天室。
  • 当评论这条消息后,仅有发送方可以收到推送。
  • 快捷评论不支持服务端抄送。
Android

监听 observeAddQuickComment 事件回调,回调中会返回接收到的快捷评论。

iOS

监听 onRecvQuickComment 事件回调,回调中会返回接收到的快捷评论。

Windows/macOS

监听 RegAddQuickCommentNotify 事件回调,回调中会返回接收到的快捷评论。

Web

监听 onQuickComment 事件回调,回调中会返回接收到的快捷评论。

如何根据msgId等信息查询历史消息?

如果您删除本地某条消息,需要查询该消息下所有子消息。 例如:该条消息已经被回复,需要到回复详情页使用这条消息(msgId)来查询所有子消息。

Android

调用 pullHistoryById 方法根据消息 ID 批量查询历史消息,主要用于消息回复场景。

iOS

调用 fetchHistoryMessages:syncToDB:completion: 方法根据消息 ID 批量查询历史消息,主要用于消息回复场景。

Windows/macOS

调用 QueryMessageOnline() 方法根据消息 ID 等信息查询历史消息,主要用于消息回复场景。

Web

调用 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 版本起,支持云端历史记录文本关键字检索,云端历史记录需要在控制台开通才可以使用,并且开通前的历史记录将不会被检索到。

Android

调用 InvocationFuture>pullMessageHistory(MsgFullKeywordSearchConfig config); 方法,其中MsgFullKeywordSearchConfigkeyword参数表示搜索关键字。

iOS

调用 - (void)retrieveServerMessages:(NIMMessageFullKeywordSearchOption *)option result:(nullable NIMRetrieveServerMessagesBlock)result; 方法进行搜素,其中NIMMessageFullKeywordSearchOptionkeyword参数表示搜索关键字。

Windows/macOS

调用 void nim::MsgLog::FullTextSearchOnlineAsync ( const FullTextSearchOnlineAsyncParam & param,const FullTextSearchOnlineAsyncCallback & cb)方法进行搜素,其中FullTextSearchOnlineAsyncParamkeyword参数表示搜索关键字。

Web

调用 msgFtsInServer(options) 方法进行搜素,其中optionkeyword参数表示搜索关键字。

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度的问题。

此文档是否对你有帮助?
有帮助
去反馈
  • 如何接收 IM 消息?
  • 如何设置和获取 IM 消息的扩展字段?
  • 如何处理消息丢失(收不到)的问题?
  • 单聊消息和群聊消息丢失
  • 聊天室消息丢失
  • 如何查看消息设置的接收方?
  • 如何判断 IM 消息发送成功?
  • 如何在单聊/群聊消息发送成功后获取消息的内容?
  • 如何对自定义消息进行安全通检测?
  • 如何获取最近会话列表?
  • 如何实现未读数清零
  • 如何设置和获取最近会话的扩展字段?
  • 收到消息时,如何获取用户资料?
  • 如何对文本消息进行关键字搜索?
  • 如何开启未读数多端同步?
  • 如何在本地插入一条消息?
  • 如何对应用内用户批量发送消息?
  • 如何配置发送消息的参数?
  • 如何获取消息附件上传下载进度?
  • 如何获取会话未读数?
  • 如何设置缩略图动图?
  • 如何标记本地消息为已读?
  • 如何过滤消息?
  • 如何删除历史消息和漫游消息?
  • 如何查询指定消息类型的历史消息?
  • 如何处理文件消息的URL?
  • 如何单向删除消息?
  • 如何接收快捷评论?
  • 如何根据msgId等信息查询历史消息?
  • 为什么获取到的历史消息数小于设置的limit?
  • 为什么Web端在撤回场景下获取到的lastmsg不准确?
  • 如何通过线上URL构建视频等附件消息?
  • 如何填写分页查询消息接口中的excludeMsgId参数?
  • 如何在发消息时透传易盾的反作弊检测参数?
  • 如何实现云端消息检索?
  • Windows下如何清空历史记录?
  • 如何实现红包/礼物消息?
  • iOS 端发送视频消息,接收方在 Web 端 Chrome 浏览器上打开黑屏
  • 问题原因
  • 解决方案