消息管理–获取消息的已读回执和送达回执

更新时间:2022-02-28

本文介绍环信即时通讯 IM SDK 从服务器获取消息的已读回执和送达回执。

环信即时通讯 IM 消息投递成功将返回送达回执,提供消息已读功能,接收方查看消息后返回已读回执。

功能 描述
消息送达回执 消息下发成功后,返回消息送达回执。
消息已读回执 接收方查看消息后,返回消息已读回执。
单个消息已读回执 提供单聊消息已读回执能力。
群组消息已读回执 提供群组消息已读回执能力。群聊回执仅旗舰版及以上支持。

使用环信即时通讯 IM SDK 可以实现消息的送达回执与已读回执,主要方法如下:

  • setRequireDeliveryAck 开启送达回执;
  • ackConversationRead 发出指定会话的已读回执;
  • ackMessageRead 发出指定消息的已读回执;
  • ackGroupMessageRead 发出群组消息的已读回执。

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

  • 完成 SDK 初始化,并连接到服务器,详见 快速开始
  • 了解环信即时通讯 IM 的使用限制,详见 使用限制

消息送达回执

若在消息送达时收到通知,你可以打开消息送达开关,这样在消息到达对方设备时你可以收到通知。

// 设置是否需要接收方送达确认,默认 `false` 即不需要。
options.setRequireDeliveryAck(false);

onMessageDelivered 回调是对方收到消息时的通知,你可以在收到该通知时,显示消息的送达状态。

EMMessageListener msgListener = new EMMessageListener() {        
    //收到消息。
    @Override    
      public void onMessageReceived(List<EMMessage> messages) { 
    }            
    //收到已送达回执。
    @Override    
      public void onMessageDelivered(List<EMMessage> message) { 
    }
};

//记得在不需要的时候移除 listener,如在 activity 的 onDestroy() 时。
EMClient.getInstance().chatManager().removeMessageListener(msgListener);

消息已读回执

消息阅读后,可以发出已读回执,该回执需要你调用 API 实现,这样消息发送方可以知道消息已被阅读。

发送会话已读回执

会话已读回执用于需要获知接收方是否阅读消息的场景,目前单聊可以直接集成,群消息已读回执功能仅旗舰版及以上支持。

注意

群消息已读回执功能为增值服务,具体使用请跳转到群消息已读回执

发送已读回执消息

推荐进入会话页面,根据会话是否有未读消息,发送会话已读回执(conversation ack),有则发送,没有则不再发送。

try {
    EMClient.getInstance().chatManager().ackConversationRead(conversationId);
} catch (HyphenateException e) {
    e.printStackTrace();
}

该方法为异步方法,需要捕捉异常。

监听已读回执回调
EMClient.getInstance().chatManager().addConversationListener(new EMConversationListener() {
        ……
        @Override
        public void onConversationRead(String from, String to) {
        //添加刷新页面通知等逻辑。
        }
});

注意

onConversationRead 中的 fromto 与消息 EMMessage 中的定义相同。

会话已读回执 onConversationRead 的场景:

(1)消息被接收方阅读,且发送了已读回执 (conversation ack); (2)多端多设备登录场景下,一端发送会话已读回执 (conversation ack),服务器端会将未读消息数置为 0,同时其他端会回调此方法。

单个消息的已读回执

注意

消息已读回执功能目前单聊 (ChatType.Chat) 可以直接集成,群聊需联系环信即时通讯 IM 的客户经理开通。单聊消息已读回执推荐使用方案为会话已读回执 (conversation ack) + 单条消息已读回执 (read ack) 结合实现,可减少发送已读回执数量。群消息已读回执功能为增值服务,具体使用请跳转到群消息已读回执

发送已读回执消息

推荐进入会话首先发送会话已读回执 (conversation ack)

try {    
    EMClient.getInstance().chatManager().ackMessageRead(conversationId);
} 
catch (HyphenateException e) {    
    e.printStackTrace();
}

在会话页面,可以在接收到消息时,根据消息类型发送消息已读回执 (read ack),如下所示:

EMClient.getInstance().chatManager().addMessageListener(new EMMessageListener() {
    ......
    

    @Override
    public void onMessageReceived(List<EMMessage> messages) {
        ......
        sendReadAck(message);
        ......
    }
    
    ......

});
/**

  * 发送已读回执。
  * @param message
    */
    public void sendReadAck(EMMessage message) {
    //这里是接收的消息,未发送过已读回执且是单聊。
    if(message.direct() == EMMessage.Direct.RECEIVE
            && !message.isAcked()
            && message.getChatType() == EMMessage.ChatType.Chat) {
        EMMessage.Type type = message.getType();
        //视频,语音及文件需要点击后再发送,可以根据需求进行调整。
        if(type == EMMessage.Type.VIDEO || type == EMMessage.Type.VOICE || type == EMMessage.Type.FILE) {
            return;
        }
        try {
            EMClient.getInstance().chatManager().ackMessageRead(message.getFrom(), message.getMsgId());
        } catch (HyphenateException e) {
            e.printStackTrace();
        }
    }
    }
    
监听单个消息的已读回执

可以调用接口监听某个消息是否已读,示例代码如下:

EMClient.getInstance().chatManager().addMessageListener(new EMMessageListener() {
    ......
    @Override
    public void onMessageRead(List<EMMessage> messages) {
        //添加刷新消息等逻辑。
    }
    ......
});

群组消息已读回执

当消息为群消息时,消息发送方(目前为管理员和群主)可以设置此消息是否需要已读回执,如需要,则设置 EMMessage 的方法 setIsNeedGroupAck()YES,之后发送。

EMMessage message = EMMessage.createTxtSendMessage(content, to);
message.setIsNeedGroupAck(true);
发送群组消息的已读回执
public void sendAckMessage(EMMessage message) {
        if (!validateMessage(message)) {
            return;
        }

        if (message.isAcked()) {
            return;
        }
    
        // May a user login from multiple devices, so do not need to send the ack msg.
        if (EMClient.getInstance().getCurrentUser().equalsIgnoreCase(message.getFrom())) {
            return;
        }
    
        try {
            if (message.isNeedGroupAck() && !message.isUnread()) {
                String to = message.conversationId(); // do not use getFrom() here
                String msgId = message.getMsgId();
                EMClient.getInstance().chatManager().ackGroupMessageRead(to, msgId, ((EMTextMessageBody)message.getBody()).getMessage());
                message.setUnread(false);
                EMLog.i(TAG, "Send the group ack cmd-type message.");
            }
        } catch (Exception e) {
            EMLog.d(TAG, e.getMessage());
        }
    }

监听群组消息已读回调

群消息已读回调在消息监听类 EMMessageListener 中。

//接收到群组消息体的已读回执, 消息的接收方已经阅读此消息。
 void onGroupMessageRead(List<EMGroupReadAck> groupReadAcks) {
      //receive group message read ack
 }

接收到群组消息已读回执后,发出消息的属性 groupAckCount 会有相应变化;

获取群组消息已读回执详情

如果想实现群消息已读回执的列表显示,可以通过下列接口获取到已读回执的详情。

/**
     * \~chinese
     * 从服务器获取群组消息回执详情。
     * 分页获取。
     * 发送群组消息回执,详见{@link #ackGroupMessageRead(String, String, String)}。
     *
     * 异步方法。
     *
     * @param msgId         消息 ID。
     * @param pageSize      每次获取群消息已读回执的条数。
     * @param startAckId    已读回执 ID,如果为空,从最新的回执向前开始获取。
     * @param callBack      结果回调,成功执行 {@link EMValueCallBack#onSuccess(Object)},失败执行{@link EMValueCallBack#onError(int, String)}。
     *
     */
    public void asyncFetchGroupReadAcks(final String msgId, final int pageSize,
                                        final String startAckId, final EMValueCallBack<EMCursorResult<EMGroupReadAck>> callBack) {
        

    }

推荐使用方案

推荐使用方案为会话已读回执 (conversation ack) + 单条消息已读回执 结合实现,可减少发送 read ack 消息量。

(1)未启动聊天页面的情况下,有未读消息,点击进入聊天页面,调用会话已读回执 (conversation ack);

(2)已经启动聊天页面内的情况下,接收到消息,即发送单条消息已读回执 (read ack) ,具体实现可参考:发送 read ack 消息