====== 消息管理–发送和接收消息 ====== 更新时间:2022-09-07 登录 Chat app 后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: * 文字消息,包含超链接和表情消息。 * 附件消息,包含图片、语音、视频及文件消息。 * 位置消息。 * CMD 消息。 * 自定义消息。 本文介绍如何使用即时通讯 IM SDK 实现发送和接收这些类型的消息。 ===== 技术原理 ===== 环信即时通讯 IM Flutter SDK 通过 ''%%EMChatManager%%'' 和 ''%%EMMessage%%'' 类实现消息的发送、接收与撤回。 其中,发送和接收消息的逻辑如下: - 发送方调用相应 Create 方法创建文本、文件、附件等类型的消息; - 发送方再调用 ''%%SendMessage%%'' 发送消息; - 接收方通过 ''%%EMChatEventHandler%%'' 中的方法监听消息回调事件。在收到 ''%%onMessagesReceived%%'' 后,即表示成功接收到消息。 ===== 前提条件 ===== 开始前,请确保满足以下条件: * 完成 SDK 初始化,详见 [[https://docs-im.easemob.com/ccim/flutter/quickstart|快速开始]]。 * 了解环信即时通讯 IM 的使用限制,详见 [[https://docs-im.easemob.com/ccim/limitation|使用限制]]。 ===== 实现方法 ===== ==== 发送消息 ==== - 首先,利用 ''%%EMMessage%%'' 类构造一个消息。 示例代码: // 设置发送的消息类型。消息类型共支持 8 种。具体详见 `MessageType` 枚举类型。 MessageType messageType = MessageType.TXT; // 设置消息接收对象 ID。 String targetId = "tom"; // 设置消息接收对象类型。消息接收对象类型包括单人、群组和聊天室。具体详见 `ChatType` 枚举类型。 ChatType chatType = ChatType.Chat; // 构造消息。构造不同类型的消息,需要不同的参数。 // 构造文本消息 EMMessage txtMsg = EMMessage.createTxtSendMessage( targetId: targetId, content: "This is text message", ); // 构建图片消息,需要图片的本地地址,长宽,和界面用来显示的名称。 String imgPath = "/data/.../image.jpg"; double imgWidth = 100; double imgHeight = 100; String imgName = "image.jpg"; int imgSize = 3000; EMMessage imgMessage = EMMessage.createImageSendMessage( targetId: targetId, filePath: imgPath, width: imgWidth, height: imgHeight, displayName: imgName, fileSize: imgSize, ); // 构建命令消息。根据命令消息可以执行具体的命令,命令的内容格式自定义的。 String action = "writing"; EMMessage cmdMsg = EMMessage.createCmdSendMessage( targetId: targetId, action: action, ); // 自定义消息。消息内容由两部分组成:消息事件和扩展字段。 // 扩展字段用户可以自行实现和使用。 String event = "gift"; Map params = {"key": "value"}; EMMessage customMsg = EMMessage.createCustomSendMessage( targetId: targetId, event: event, params: params, ); // 构建文件消息。文件消息主要需要本地文件地址和文件在页面显示的名称。 String filePath = "data/.../foo.zip"; String fileName = "foo.zip"; int fileSize = 6000; EMMessage fileMsg = EMMessage.createFileSendMessage( targetId: targetId, filePath: filePath, displayName: fileName, fileSize: fileSize, ); // 构建位置消息。位置消息可以传递经纬度和地名信息。 // 当你需要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 double latitude = 114.78; double longitude = 39.89; String address = "darwin"; EMMessage.createLocationSendMessage( targetId: targetId, latitude: latitude, longitude: longitude, address: address, ); // 构建视频消息。视频消息主要由视频和视频缩略图组成,视频消息支持输入视频的首帧作为缩略图。视频参数包括视频本地地址、视频长宽值,显示名称,播放时间长度。 // 视频的缩略图需要缩略图的本地地址。 // 视频消息相当于包含 2 个附件的消息。 String videoPath = "data/.../foo.mp4"; double videoWidth = 100; double videoHeight = 100; String videoName = "foo.mp4"; int videoDuration = 5; int videoSize = 4000; EMMessage.createVideoSendMessage( targetId: targetId, filePath: videoPath, width: videoWidth, height: videoHeight, duration: videoDuration, fileSize: videoSize, displayName: videoName, ); // 构建语音消息。该消息需要本地语音文件地址、显示名称和播放时长。 String voicePath = "data/.../foo.wav"; String voiceName = "foo.wav"; int voiceDuration = 5; int voiceSize = 1000; EMMessage.createVoiceSendMessage( targetId: targetId, filePath: voicePath, duration: voiceDuration, fileSize: voiceSize, displayName: voiceName, ); EMMessage message = EMMessage.createCmdSendMessage(targetId: targetId, action: "action");
  1. 通过 ''%%EMChatManager%%'' 将该消息发出。
EMClient.getInstance.chatManager.sendMessage(message).then((value) { }); 你可以设置发送消息结果回调,用于接收消息发送进度或者发送结果,如发送成功或失败。为此,需实现 ''%%MessageStatusCallBack%%'' 接口。 对于命令消息可能并不需要结果,可以不用赋值。 // 实现回调对象 message.setMessageStatusCallBack( MessageStatusCallBack( onSuccess: () { // 收到成功回调之后,可以对发送的消息进行更新处理,或者其他操作。 }, onError: (error) { // 收到回调之后,可以将发送的消息状态进行更新,或者进行其他操作。 }, onProgress: (progress) { // 对于附件类型的消息,如图片,语音,文件,视频类型,上传或下载文件时会收到相应的进度值,表示附件的上传或者下载进度。 }, ), ); // 消息发送结果会通过回调对象返回,该返回结果仅表示该方法的调用结果,与实际消息发送状态无关。 EMClient.getInstance.chatManager.sendMessage(message).then((value) { // 消息发送动作完成。 }); ==== 接收消息 ==== 你可以添加 ''%%EMChatEventHandler%%'' 监听器接收消息。 该 ''%%EMChatEventHandler%%'' 可以多次添加。请记得在不需要的时候移除该监听器,如在 ''%%dispose%%'' 的卸载组件的时候。 在新消息到来时,你会收到 ''%%onMessagesReceived%%'' 的事件,消息接收时可能是一条,也可能是多条。你可以在该回调里遍历消息队列,解析并显示收到的消息。 // 添加监听 EMClient.getInstance.chatManager.addEventHandler( "UNIQUE_HANDLER_ID", EMChatEventHandler( onMessagesReceived: (messages) {}, ), ); // 移除监听 EMClient.getInstance.chatManager.removeEventHandler("UNIQUE_HANDLER_ID"); 文件消息不会自动下载。图片消息和视频消息默认会生成缩略图,接收到图片消息和视频消息,默认会自动下载缩略图。语音消息接收到后会自动下载。 若手动下载附件,需进行如下设置: - 在初始化 SDK 时将 ''%%isAutoDownloadThumbnail%%'' 设置为 ''%%false%%''。 EMOptions options = EMOptions( appKey: appKey, isAutoDownloadThumbnail: false, );
  1. 设置下载监听器。
message.setMessageStatusCallBack( MessageStatusCallBack( onProgress: (progress) {}, onSuccess: () {}, onError: (e) {}, ), );
  1. 下载缩略图。
EMClient.getInstance.chatManager.downloadThumbnail(message); 下载完成后,调用相应消息 body 的 ''%%thumbnailLocalPath%%'' 获取缩略图路径。 // 获取图片消息 body。 EMImageMessageBody imgBody = message.body as EMImageMessageBody; // 从本地获取图片缩略图路径。 String? thumbnailLocalPath = imgBody.thumbnailLocalPath;
  1. 下载附件。
EMClient.getInstance.chatManager.downloadAttachment(message); 下载完成后,调用相应消息 body 的 ''%%localPath%%'' 获取附件路径。 示例代码如下: // 获取文件消息 body。 EMFileMessageBody fileBody = message.body as EMFileMessageBody; // 从本地获取文件路径。 String? localPath = fileBody.localPath; ==== 撤回消息 ==== 消息发送后 2 分钟之内,消息的发送方可以撤回该消息。如果需要调整可撤回时限,可以联系商务。 try { await EMClient.getInstance.chatManager.recallMessage(msgId); } on EMError catch (e) { } 设置消息撤回监听器: // 消息被撤回时触发的回调(此回调位于 EMChatEventHandler 中)。 void onMessagesRecalled(List messages) {} ==== 更多操作 ==== 你可以参考如下文档,在项目中实现更多的消息相关功能: * [[https://docs-im.easemob.com/ccim/flutter/message1|消息概述]]; * [[https://docs-im.easemob.com/ccim/flutter/message3|管理本地消息数据]]; * [[https://docs-im.easemob.com/ccim/flutter/message4|从服务器获取会话和消息(消息漫游)]]; * [[https://docs-im.easemob.com/ccim/flutter/message5|获取消息的已读回执和送达回执]]; * [[https://docs-im.easemob.com/ccim/flutter/translation|实现翻译功能]]。