接收消息

大约 12 分钟

接收消息

功能说明

环信即时通讯 IM HarmonyOS SDK 通过 ChatMessageListener 接收文本、图片、语音、视频、文件、位置、透传、自定义和合并等类型的消息。应用在消息监听回调中识别消息类型,读取对应消息体并根据业务需要展示或处理消息。

前提条件

开始前,请确保满足以下条件:

监听消息事件

应用通过 ChatManager#addMessageListener 注册 ChatMessageListener。收到普通消息时触发 onMessageReceived;收到透传消息时触发 onCmdMessageReceived。同一个 ChatManager 可以注册多个消息监听器,不再使用时应及时移除。

let messageListener: ChatMessageListener = {
  onMessageReceived: (messages: Array<ChatMessage>): void => {
    // 处理文本、附件、位置、自定义及合并消息。
  },
  onCmdMessageReceived: (messages: Array<ChatMessage>): void => {
    // 处理透传消息。
  }
};

let chatManager = ChatClient.getInstance().chatManager();
chatManager?.addMessageListener(messageListener);

// 不再使用时移除同一个监听器实例。
chatManager?.removeMessageListener(messageListener);

消息通用信息

收到消息后,可通过以下 ChatMessage 方法读取消息的通用信息:

字段或属性获取方式说明
消息 IDChatMessage#getMsgId消息的唯一 ID。
发送方ChatMessage#getFrom消息发送者的用户 ID。
接收方ChatMessage#getTo消息目标 ID。
会话 IDChatMessage#getConversationId消息所属会话的 ID。
会话类型ChatMessage#getChatType单聊、群聊或聊天室。
消息类型ChatMessage#getType文本、图片、语音、视频、文件等类型。
消息体ChatMessage#getBody获取对应的 ChatMessageBody 子类;消息体不存在时返回 undefined。
扩展字段ChatMessage#ext获取发送方通过 setExt 设置的业务扩展字段,返回 Map<string, MessageExtType>。
发送方信息ChatMessage#getSenderInfo开启用户信息自动管理后,可获取发送方昵称、头像、群名片和好友备注等信息;消息未携带时返回 undefined。

接收文本消息

收到 onMessageReceived 回调后,遍历消息列表。对于文本消息,将消息体转换为 TextMessageBody,调用 getContent() 获取文本内容;如需读取业务扩展字段,可调用 ChatMessage#ext 获取扩展字段映射。

let messageListener: ChatMessageListener = {
  onMessageReceived: (messages: Array<ChatMessage>): void => {
    messages.forEach((message: ChatMessage): void => {
      // 过滤非文本消息。
      if (message.getType() !== ContentType.TXT) {
        return;
      }

      // 获取文本消息体。
      let textBody = message.getBody() as TextMessageBody;
      // 获取文本内容。
      let text = textBody.getContent();

      // 获取消息中的全部扩展字段。
      let ext = message.ext();
      // 按业务字段名读取扩展字段。
      let businessValue = ext.get('businessKey');

      // 根据 text 和 businessValue 更新界面或执行业务逻辑。
    });
  }
};

// 注册消息监听器。
ChatClient.getInstance()
  .chatManager()
  ?.addMessageListener(messageListener);

接收附件消息

除文本消息外,SDK 还支持接收附件类型消息,包括语音、图片、视频和文件消息。

附件消息的接收过程如下:

  1. 接收附件消息。默认情况下,SDK 自动下载语音附件,并根据配置自动下载图片和视频的缩略图。原始图片附件、大图、视频和文件需要按业务需要调用对应下载接口。
  2. 从消息体读取附件的服务器地址、本地路径及下载状态。

ChatManager#downloadAttachment、downloadThumbnail 和 downloadBigImage 均返回 Promise<void>,并支持可选的下载进度回调。进度值范围为 0-100。

接收语音消息

  1. 默认情况下,接收方收到语音消息时,SDK 自动下载语音附件。
  2. 接收方收到 onMessageReceived 回调后,通过 VoiceMessageBody#getRemoteUrl 或 getLocalPath 获取语音附件的服务器地址或本地路径。
// 获取语音消息体。
let voiceBody = message.getBody() as VoiceMessageBody;

// 语音附件的服务器地址。
let voiceRemoteUrl = voiceBody.getRemoteUrl();
// 语音附件的本地路径。
let voiceLocalPath = voiceBody.getLocalPath();
// 语音时长,单位为秒。
let duration = voiceBody.getDuration();

接收图片消息

一条图片消息通常包含三类图片资源:

  • 原图:发送方本地选择的原始图片文件,通常用于查看或保存原图。
  • 大图:SDK 客户端基于原图进行等比压缩后上传的图片。压缩规则为:若图片短边大于 720 像素,则等比压缩至短边为 720 像素;若短边小于等于 720 像素,则保留原图尺寸,不做放大处理。此类图片通常用于聊天详情页展示。
  • 缩略图:服务端基于原图进行等比压缩后的图片。压缩规则为:默认情况下,若图片短边大于 170 像素,则等比压缩至短边为 170 像素;若短边小于等于 170 像素,则保留原图尺寸,不做放大处理。缩略图的压缩方式和尺寸可在 控制台进行配置。此类图片通常用于会话列表、聊天列表等轻量展示场景。

收到图片消息后,SDK 会根据配置自动下载缩略图。若业务需要显示更清晰的图片,可再按需下载大图或图片附件。

接收图片消息的流程如下:

  1. SDK 根据初始化配置决定是否自动下载缩略图:

    • ChatOptions#setAutoDownloadThumbnail 默认为 true,SDK 自动下载图片缩略图。
    • 设置为 false 后,应用需调用 ChatManager#downloadThumbnail 手动下载缩略图。
  2. 在 onMessageReceived 中识别图片消息,并根据业务需要下载资源:

    • 调用 ChatManager#downloadAttachment 下载图片附件。若发送方发送原图,该附件为原图;若发送方发送大图,该附件为发送方生成的大图。
    • 调用 ChatManager#downloadBigImage 下载大图。该方法仅适用于图片消息。

如果本地已存在对应资源,建议直接复用本地文件,避免重复下载。

/**
 * 下载图片的大图或图片附件。
 * useBigImage 为 true 时下载大图,为 false 时下载图片附件。
 */
function downloadImage(message: ChatMessage, useBigImage: boolean): void {
  let chatManager = ChatClient.getInstance().chatManager();
  if (!chatManager) {
    return;
  }

  let downloadTask: Promise<void>;
  if (useBigImage) {
    // 下载大图。
    downloadTask = chatManager.downloadBigImage(
      message,
      (progress: number): void => {
        // 大图下载进度。
      }
    );
  } else {
    // 下载图片附件。
    downloadTask = chatManager.downloadAttachment(
      message,
      (progress: number): void => {
        // 图片附件下载进度。
      }
    );
  }

  downloadTask.then((): void => {
    // 图片资源下载成功。
  }).catch((error: ChatError): void => {
    // 图片资源下载失败。
  });
}
  1. 通过 ImageMessageBody 获取图片附件、大图和缩略图的服务器地址、本地路径和状态:
// 获取图片消息体。
let imageBody = message.getBody() as ImageMessageBody;

// true 表示图片附件为原图;false 表示为发送方压缩后的大图。
let isOriginal = imageBody.isOriginalImage();

// 获取图片附件的服务器地址和本地路径。
let imageRemoteUrl = imageBody.getRemoteUrl();
let imageLocalPath = imageBody.getLocalPath();

// 获取大图的服务器地址和本地路径。
let bigImageRemoteUrl = imageBody.getBigImageRemoteUrl();
let bigImageLocalPath = imageBody.getBigImageLocalPath();

// 获取缩略图的服务器地址和本地路径。
let thumbnailRemoteUrl = imageBody.getThumbnailRemoteUrl();
let thumbnailLocalPath = imageBody.getThumbnailLocalPath();

// 获取图片附件、大图和缩略图的下载状态。
let attachmentStatus = imageBody.getDownloadStatus();
let bigImageStatus = imageBody.getBigImageDownloadStatus();
let thumbnailStatus = imageBody.getThumbnailDownloadStatus();

// 获取图片宽高。
let width = imageBody.getWidth();
let height = imageBody.getHeight();

接收 GIF 图片消息

GIF 图片缩略图的下载方式与普通图片消息相同,详见 接收图片消息。

接收方在 onMessageReceived 中识别图片消息后,可调用 ImageMessageBody#isGif 判断是否为 GIF 图片。

let messageListener: ChatMessageListener = {
  onMessageReceived: (messages: Array<ChatMessage>): void => {
    messages.forEach((message: ChatMessage): void => {
      // 过滤非图片消息。
      if (message.getType() !== ContentType.IMAGE) {
        return;
      }

      // 获取图片消息体。
      let body = message.getBody() as ImageMessageBody;
      // 判断是否为 GIF 图片。
      if (body.isGif()) {
        // 根据业务需要下载并展示 GIF 图片。
      }
    });
  }
};

接收视频消息

收到视频消息后,通常先在聊天界面展示视频缩略图;用户点击消息时,再下载或播放视频原文件。

接收视频消息的流程如下:

  1. SDK 根据 ChatOptions#setAutoDownloadThumbnail 的配置决定是否自动下载视频缩略图。该选项默认为 true;关闭后,需调用 ChatManager#downloadThumbnail 手动下载。详见 接收图片消息。
  2. SDK 通过 onMessageReceived 将视频消息传递给应用。可优先使用缩略图展示预览;用户播放视频时,再调用 ChatManager#downloadAttachment 下载视频原文件。
  3. 下载前建议检查本地路径是否已有可用文件,避免重复下载。
function downloadVideo(message: ChatMessage): void {
  let chatManager = ChatClient.getInstance().chatManager();
  if (!chatManager) {
    return;
  }

  // 下载视频原文件。
  chatManager.downloadAttachment(
    message,
    (progress: number): void => {
      // 视频附件下载进度。
    }
  ).then((): void => {
    // 视频附件下载成功,可播放或保存视频。
  }).catch((error: ChatError): void => {
    // 视频附件下载失败。
  });
}
  1. 通过 VideoMessageBody 获取视频原文件和缩略图的服务器地址或本地路径:
// 获取视频消息体。
let videoBody = message.getBody() as VideoMessageBody;

// 获取视频原文件的服务器地址和本地路径。
let videoRemoteUrl = videoBody.getRemoteUrl();
let videoLocalPath = videoBody.getLocalPath();

// 获取视频缩略图的服务器地址和本地路径。
let thumbnailRemoteUrl = videoBody.getThumbnailRemoteUrl();
let thumbnailLocalPath = videoBody.getThumbnailLocalPath();

// 获取视频时长、视频原文件下载状态和缩略图下载状态。
let duration = videoBody.getDuration();
let attachmentStatus = videoBody.getDownloadStatus();
let thumbnailStatus = videoBody.getThumbnailDownloadStatus();

接收文件消息

接收文件消息的流程如下:

  1. 接收方收到 onMessageReceived 回调后,调用 ChatManager#downloadAttachment 下载文件。
function downloadFile(message: ChatMessage): void {
  let chatManager = ChatClient.getInstance().chatManager();
  if (!chatManager) {
    return;
  }

  // 下载文件附件。
  chatManager.downloadAttachment(
    message,
    (progress: number): void => {
      // 文件下载进度。
    }
  ).then((): void => {
    // 文件下载成功。
  }).catch((error: ChatError): void => {
    // 文件下载失败。
  });
}
  1. 通过 FileMessageBody 获取文件的服务器地址、本地路径和其他属性:
// 获取文件消息体。
let fileBody = message.getBody() as FileMessageBody;

// 获取文件的服务器地址和本地路径。
let fileRemoteUrl = fileBody.getRemoteUrl();
let fileLocalPath = fileBody.getLocalPath();
// 获取文件名和文件大小。
let fileName = fileBody.getFileName();
let fileLength = fileBody.getFileLength();
// 获取文件下载状态。
let downloadStatus = fileBody.getDownloadStatus();

接收位置消息

接收位置消息与文本消息的监听方式相同,详见 接收文本消息。

应用将消息体转换为 LocationMessageBody,通过 getLatitude()、getLongitude()、getAddress() 和 getBuildingName() 获取位置数据,再使用第三方地图服务展示位置。

// 获取位置消息体。
let locationBody = message.getBody() as LocationMessageBody;

// 获取纬度和经度。
let latitude = locationBody.getLatitude();
let longitude = locationBody.getLongitude();
// 获取地址和建筑物名称。
let address = locationBody.getAddress();
let buildingName = locationBody.getBuildingName();

接收透传消息

可将透传消息理解为一条指令,通过发送这条指令给对方,通知对方要执行的操作,收到消息可以自定义处理。

具体功能可以根据自身业务需求自定义。另外,action 不能以 em_ 或 easemob:: 开头,这两个前缀为内部保留字段。

提示

  • 透传消息发送后不支持撤回。
  • 透传消息不会存入本地数据库,也不会创建本地会话,因此通常不在 UI 中显示。

透传消息通过 ChatMessageListener#onCmdMessageReceived 回调,而不是普通消息的 onMessageReceived 回调。将消息体转换为 CmdMessageBody 后,可通过 action() 获取命令动作。

let messageListener: ChatMessageListener = {
  onMessageReceived: (messages: Array<ChatMessage>): void => {
    // 接收普通消息。
  },
  onCmdMessageReceived: (messages: Array<ChatMessage>): void => {
    // 接收透传消息。
    messages.forEach((message: ChatMessage): void => {
      // 获取透传消息体。
      let body = message.getBody() as CmdMessageBody;
      // 获取命令动作。
      let action = body.action();
      // 根据 action 执行业务逻辑。
    });
  }
};

接收自定义类型消息

你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。

接收自定义消息与其他类型消息一致,应用在 onMessageReceived 中识别 ContentType.CUSTOM 消息,将消息体转换为 CustomMessageBody,再通过 event() 获取自定义事件,通过 getParams() 获取自定义参数。

// 判断是否为自定义消息。
if (message.getType() === ContentType.CUSTOM) {
  // 获取自定义消息体。
  let customBody = message.getBody() as CustomMessageBody;
  // 获取自定义事件。
  let event = customBody.event();
  // 获取自定义参数。
  let params = customBody.getParams();

  // 根据 event 和 params 执行业务逻辑。
}

接收合并消息

接收合并消息与接收普通消息的流程相同,应用在 onMessageReceived 中识别 ContentType.COMBINE 消息。

  • 对于不支持合并消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 compatibleText,其他字段会被忽略。
  • 合并消息实际上是一种附件消息。收到合并消息后,可以调用 ChatManager#downloadAndParseCombineMessage 下载并解析合并消息附件,获取原始消息列表。
  • 首次调用该方法时,SDK 会下载并解析合并消息附件,然后返回其中包含的原始消息列表。
  • 后续调用该方法时:
    • 若本地附件已存在,SDK 会直接解析附件并返回原始消息列表。
    • 若本地附件不存在,SDK 会重新下载附件,解析后返回原始消息列表。

通过 CombineMessageBody 还可以读取合并消息的标题、摘要和兼容文本。

// 判断是否为合并消息。
if (message.getType() === ContentType.COMBINE) {
  // 获取合并消息体。
  let body = message.getBody() as CombineMessageBody;
  // 获取合并消息的标题、摘要和兼容文本。
  let title = body.getTitle();
  let summary = body.getSummary();
  let compatibleText = body.getCompatibleText();

  // 下载并解析合并消息附件。
  ChatClient.getInstance()
    .chatManager()
    ?.downloadAndParseCombineMessage(message)
    .then((messages: Array<ChatMessage>): void => {
      // 处理并展示原始消息列表。
    })
    .catch((error: ChatError): void => {
      // 处理下载或解析错误。
    });
}

更多

消息接收回调返回发送成功的消息

在 SDK 初始化前调用 ChatOptions#setIncludeSendMessageInMessageListener(true) 后,本端发送成功的消息也会通过 onMessageReceived 回调返回。该选项默认为 false,此时 onMessageReceived 仅返回接收到的消息。

判断消息是否为聊天室广播消息

对于聊天室消息,可以通过 ChatMessage#isBroadcast 判断该消息是否为 通过 REST API 发送的聊天室全局广播消息。

消息附件下载鉴权

环信即时通讯 IM 支持消息附件下载鉴权功能。该功能默认关闭,如需开通请联系环信商务。功能开通后,应用必须通过 SDK 的 downloadAttachment、downloadThumbnail 或 downloadBigImage 等接口下载相应附件,不能直接使用附件 URL 下载。

接口列表

API 名称所属模块/类说明
addMessageListenerChatManager注册消息监听器。
removeMessageListenerChatManager移除消息监听器。
downloadThumbnailChatManager下载图片或视频缩略图。
downloadBigImageChatManager下载图片大图。
downloadAttachmentChatManager下载图片附件、语音、视频或文件附件。
downloadAndParseCombineMessageChatManager下载并解析合并消息。
getThumbnailLocalPathImageMessageBody获取图片缩略图的本地路径。
getBigImageLocalPathImageMessageBody获取图片大图的本地路径。
上次编辑于: