消息


发送文本、语音、图片、位置等消息(单聊/群聊通用)。

发送文本消息

//创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此
EMMessage message = EMMessage.createTxtSendMessage(content, toChatUsername);
//如果是群聊,设置chattype,默认是单聊
if (chatType == CHATTYPE_GROUP)
	message.setChatType(ChatType.GroupChat);
//发送消息
EMClient.getInstance().chatManager().sendMessage(message);

发送表情消息

发表情消息实质上是发文本消息。接收方收到文本消息后,首先查询文本消息是否是表情消息,如果是,则显示该文本消息为对应的表情图片。可以参考emoji列表来做表情图片和对应的文本字符串的映射。也可以自行维护表情图片和文本字符串的映射。

//创建一条表情消息。表情消息实质上是一个文字消息。emojiCode是表情图片对应的文本字符串,toChatUsername为对方用户或者群聊的id,后文皆是如此
EMMessage message = EMMessage.createTxtSendMessage(emojiCode, toChatUsername);
//如果是群聊,设置chattype,默认是单聊
if (chatType == CHATTYPE_GROUP)
	message.setChatType(ChatType.GroupChat);
//发送消息
EMClient.getInstance().chatManager().sendMessage(message);

发送语音消息

//voiceUri为语音文件本地资源标志符,length为录音时间(秒) 
EMMessage message = EMMessage.createVoiceSendMessage(voiceUri, length, toChatUsername); 
//如果是群聊,设置chattype,默认是单聊 
if (chatType == CHATTYPE_GROUP) 
    message.setChatType(ChatType.GroupChat); 
EMClient.getInstance().chatManager().sendMessage(message);

发送成功后,获取语音消息附件:

EMVoiceMessageBody voiceBody = (EMVoiceMessageBody) msg.getBody();
//获取语音文件在服务器的地址
String voiceRemoteUrl = voiceBody.getRemoteUrl();
//本地语音文件的资源路径
Uri voiceLocalUri = voiceBody.getLocalUri();

适配AndroidQ及以上手机时,获取本地资源请调用voiceBody.getLocalUri(),相应的voiceBody.getLocalUrl()方法已经被废弃!

发送视频消息

//videoLocalUri为视频本地资源标志符,thumbLocalUri为视频预览图路径,videoLength为视频时间长度
EMMessage message = EMMessage.createVideoSendMessage(videoLocalUri, thumbLocalUri, videoLength, toChatUsername);
//如果是群聊,设置chattype,默认是单聊
if (chatType == CHATTYPE_GROUP)
   message.setChatType(ChatType.GroupChat);
EMClient.getInstance().chatManager().sendMessage(message);

发送成功后,获取视频消息缩略图及附件

EMVideoMessageBody videoBody = (EMVideoMessageBody) message.getBody();
//获取视频文件在服务器的路径
String videoRemoteUrl = videoBody.getRemoteUrl();
//获取缩略图在服务器的路径
String thumbnailUrl = videoBody.getThumbnailUrl();
//本地视频文件的资源路径
Uri videoLocalUri = videoBody.getLocalUri();
//本地视频缩略图资源路径
Uri localThumbUri = videoBody.getLocalThumbUri();

适配AndroidQ及以上手机时,获取本地资源请调用videoBody.getLocalUri(),相应的videoBody.getLocalUrl()方法已经被废弃!

发送图片消息

//imageUri为图片本地资源标志符,false为不发送原图(默认超过100k的图片会压缩后发给对方),需要发送原图传true 
EMMessage.createImageSendMessage(imageUri, false, toChatUsername); 
//如果是群聊,设置chattype,默认是单聊 
if (chatType == CHATTYPE_GROUP) 
    message.setChatType(ChatType.GroupChat); 
EMClient.getInstance().chatManager().sendMessage(message);

发送成功后,获取图片消息缩略图及附件

EMImageMessageBody imgBody = (EMImageMessageBody) message.getBody();
//获取图片文件在服务器的路径
String imgRemoteUrl = imgBody.getRemoteUrl();
//获取图片缩略图在服务器的路径
String thumbnailUrl = imgBody.getThumbnailUrl();
//本地图片文件的资源路径
Uri imgLocalUri = imgBody.getLocalUri();
//本地图片缩略图资源路径
Uri thumbnailLocalUri = imgBody.thumbnailLocalUri();

适配AndroidQ及以上手机时,获取本地资源请调用imgBody.getLocalUri(),相应的imgBody.getLocalUrl()方法已经被废弃!

发送地理位置消息

//latitude为纬度,longitude为经度,locationAddress为具体位置内容
EMMessage message = EMMessage.createLocationSendMessage(latitude, longitude, locationAddress, toChatUsername);
//如果是群聊,设置chattype,默认是单聊
if (chatType == CHATTYPE_GROUP)
	message.setChatType(ChatType.GroupChat);
EMClient.getInstance().chatManager().sendMessage(message);

发送文件消息

//fileLocalUri为本地资源标志符
EMMessage message = EMMessage.createFileSendMessage(fileLocalUri, toChatUsername);
// 如果是群聊,设置chattype,默认是单聊
if (chatType == CHATTYPE_GROUP)
    message.setChatType(ChatType.GroupChat);
EMClient.getInstance().chatManager().sendMessage(message);

发送成功后,获取文件消息附件

EMNormalFileMessageBody fileMessageBody = (EMNormalFileMessageBody) message.getBody();
//获取文件在服务器的路径
String fileRemoteUrl = fileMessageBody.getRemoteUrl();
//本地文件的资源路径
Uri fileLocalUri = fileMessageBody.getLocalUri();

适配AndroidQ及以上手机时,获取本地资源请调用fileMessageBody.getLocalUri(),相应的fileMessageBody.getLocalUrl()方法已经被废弃!

发送透传消息

透传消息能做什么:头像、昵称的更新等。可以把透传消息理解为一条指令,通过发送这条指令给对方,告诉对方要做的 action,收到消息可以自定义处理的一种消息。(透传消息不会存入本地数据库中,所以在 UI 上是不会显示的)。另,以“em_”和“easemob::”开头的action为内部保留字段,注意不要使用

EMMessage cmdMsg = EMMessage.createSendMessage(EMMessage.Type.CMD);

//支持单聊和群聊,默认单聊,如果是群聊添加下面这行
cmdMsg.setChatType(ChatType.GroupChat)
String action="action1";//action可以自定义
EMCmdMessageBody cmdBody = new EMCmdMessageBody(action);
String toUsername = "test1";//发送给某个人
cmdMsg.setTo(toUsername);
cmdMsg.addBody(cmdBody); 
EMClient.getInstance().chatManager().sendMessage(cmdMsg);

发送自定义类型消息

用户可以在以上几种消息之外,自己定义消息类型,方便用户的业务处理。 自定义消息类型支持用户自己设置一个消息的类型名称,这样用户可以添加多种自定义消息。 自定义消息的内容部分是key,value格式的,用户需要自己添加并解析该内容。

EMMessage customMessage = EMMessage.createSendMessage(EMMessage.Type.CUSTOM);
// event为需要传递的自定义消息事件,比如礼物消息,可以设置event = "gift"
EMCustomMessageBody customBody = new EMCustomMessageBody(event);
// params类型为Map<String, String>
customBody.setParams(params);
customMessage.addBody(customBody);
// to指另一方环信id(或者群组id,聊天室id)
customMessage.setTo(to);
// 如果是群聊,设置chattype,默认是单聊
customMessage.setChatType(chatType);
EMClient.getInstance().chatManager().sendMessage(customMessage);
        

设置群消息是否需要已读回执(增值服务)

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

public EMMessage createDingMessage(String to, String content) {
        EMMessage message = EMMessage.createTxtSendMessage(content, to);
        message.setIsNeedGroupAck(true);
        return message;
    }
    
 

发送群消息已读回执

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 user 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());
        }
    }

当发送群已读回执后,消息发送方对应EMMessage的groupAckCount属性会有相应变化;

群消息已读回调

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

/**
	 * \~chinese
	 * 接受到群组消息体的已读回执, 消息的接收方已经阅读此消息。
	 */
         void onGroupMessageRead(List<EMGroupReadAck> groupReadAcks) {
        }

获取群消息已读回执详情

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

/**
	 * \~chinese
	 * 从服务器获取群组消息回执详情
	 * @param msgId 消息id
	 * @param pageSize 获取的页面大小
	 * @param startAckId 已读回执的id,如果为空,从最新的回执向前开始获取
	 * @return 返回消息列表和用于继续获取群消息回执的Cursor
	 */
	public void asyncFetchGroupReadAcks(final String msgId, final int pageSize,
			final String startAckId, final EMValueCallBack<EMCursorResult<EMGroupReadAck>> callBack) {

	}

发送扩展消息

当 SDK 提供的消息类型不满足需求时,开发者可以通过扩展自 SDK 提供的文本、语音、图片、位置等消息类型,从而生成自己需要的消息类型。

这里是扩展自文本消息,如果这个自定义的消息需要用到语音或者图片等,可以扩展自语音、图片消息,亦或是位置消息。

EMMessage message = EMMessage.createTxtSendMessage(content, toChatUsername);
 
// 增加自己特定的属性
message.setAttribute("attribute1", "value");
message.setAttribute("attribute2", true);
...
EMClient.getInstance().chatManager().sendMessage(message);

//接收消息的时候获取到扩展属性
//获取自定义的属性,第2个参数为没有此定义的属性时返回的默认值
message.getStringAttribute("attribute1",null);
message.getBooleanAttribute("attribute2", false);
...

通过注册消息监听来接收消息。

EMClient.getInstance().chatManager().addMessageListener(msgListener);
EMMessageListener msgListener = new EMMessageListener() {
	
	@Override
	public void onMessageReceived(List<EMMessage> messages) {
		//收到消息
	}
	
	@Override
	public void onCmdMessageReceived(List<EMMessage> messages) {
		//收到透传消息
	}
	
	@Override
	public void onMessageRead(List<EMMessage> messages) {
		//收到已读回执
	}
	
	@Override
	public void onMessageDelivered(List<EMMessage> message) {
		//收到已送达回执
	}
       @Override
	public void onMessageRecalled(List<EMMessage> messages) {
		//消息被撤回
	}
	
	@Override
	public void onMessageChanged(EMMessage message, Object change) {
		//消息状态变动
	}
};

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

下载缩略图

如果设置了自动下载,即EMClient.getInstance().getOptions().getAutodownloadThumbnail()为true,SDK接收到消息后会下载缩略图;
如果没有设置自动下载,需主动调用EMClient.getInstance().chatManager().downloadThumbnail(message)下载。
下载完成后,调用相应消息body的thumbnailLocalUri()去获取缩略图路径。
例如:

EMImageMessageBody imgBody = (EMImageMessageBody) message.getBody();
//本地图片缩略图资源路径
Uri thumbnailLocalUri = imgBody.thumbnailLocalUri();

下载附件

下载附件的方法为:EMClient.getInstance().chatManager().downloadAttachment(message);
下载完成后,调用相应消息body的getLocalUri()去获取附件路径。
例如:

EMImageMessageBody imgBody = (EMImageMessageBody) message.getBody();
//本地图片文件的资源路径
Uri imgLocalUri = imgBody.getLocalUri();

通过 message 设置消息的发送及接收状态。 注意:需在sendMessage之前去设置此回调监听

message.setMessageStatusCallback(new EMCallBack(){});
EMConversation conversation = EMClient.getInstance().chatManager().getConversation(username);
//获取此会话的所有消息
List<EMMessage> messages = conversation.getAllMessages();
//SDK初始化加载的聊天记录为20条,到顶时需要去DB里获取更多
//获取startMsgId之前的pagesize条消息,此方法获取的messages SDK会自动存入到此会话中,APP中无需再次把获取到的messages添加到会话中
List<EMMessage> messages = conversation.loadMoreMsgFromDB(startMsgId, pagesize);
EMConversation conversation = EMClient.getInstance().chatManager().getConversation(username);
conversation.getUnreadMsgCount();
EMClient.getInstance().chatManager().getUnreadMessageCount();
EMConversation conversation = EMClient.getInstance().chatManager().getConversation(username);
//指定会话消息未读数清零
conversation.markAllMessagesAsRead();
//把一条消息置为已读
conversation.markMessageAsRead(messageId);
//所有未读消息数清零
EMClient.getInstance().chatManager().markAllConversationsAsRead();
EMConversation conversation = EMClient.getInstance().chatManager().getConversation(username);
//获取此会话在本地的所有的消息数量
conversation.getAllMsgCount();
//如果只是获取当前在内存的消息数量,调用
conversation.getAllMessages().size();

环信sdk 在3.3.4版本增加了一个消息漫游接口,即可以从服务器拉取历史消息到本地,方便用户切换设备同步消息(此功能属于增值服务,需要联系商务同事开通

此方法属于EMChatManager 类,通过EMClient.getInstance().chatManager()调用,使用方法参考 Demo 中 EaseChatFragment 类的 loadMoreRoamingMessages() 方法

/**
	 * 从服务器获取历史消息
	 *
	 * @param conversationId 会话名称
	 * @param type 会话类型
	 * @param pageSize 获取的页面大小
	 * @param startMsgId 漫游消息的开始消息id,如果为空,从最新的消息向前开始获取
	 * @return 返回消息列表和用于继续获取历史消息的Cursor
	 */
	public EMCursorResult<EMMessage> fetchHistoryMessages(String conversationId, EMConversationType type, int pageSize,String startMsgId);

	/**
	 * 从服务器获取历史消息
	 * 
	 * @param conversationId 会话名称
	 * @param type 会话类型
	 * @param pageSize 获取的页面大小
	 * @param startMsgId 漫游消息的开始消息id,如果为空,从最新的消息向前开始获取
	 * @param callBack 返回消息列表和用于继续获取历史消息的Cursor
	 */
	public void asyncFetchHistoryMessage(String conversationId, EMConversationType type, int pageSize, String startMsgId, EMValueCallBack<EMCursorResult<EMMessage>> callBack) 

消息撤回功能可以撤回一定时间内发送出去的消息,消息撤回时限默认2分钟,可根据开发者需求以AppKey为单位进行单独设置,如需修改请联系环信商务。

消息撤回为增值功能,请联系环信商务开通。

EMClient.getInstance().chatManager().recallMessage(contextMenuMessage);
try {
    EMClient.getInstance().chatManager().fetchHistoryMessages(
            toChatUsername, EaseCommonUtils.getConversationType(chatType), pagesize, "");
    final List<EMMessage> msgs = conversation.getAllMessages();
    int msgCount = msgs != null ? msgs.size() : 0;
    if (msgCount < conversation.getAllMsgCount() && msgCount < pagesize) {
        String msgId = null;
        if (msgs != null && msgs.size() > 0) {
            msgId = msgs.get(0).getMsgId();
        }
        conversation.loadMoreMsgFromDB(msgId, pagesize - msgCount);
    }
    messageList.refreshSelectLast();
} catch (HyphenateException e) {
    e.printStackTrace();
}
Map<String, EMConversation> conversations = EMClient.getInstance().chatManager().getAllConversations();

如果出现偶尔返回的conversations的sizi为0,那很有可能是没有调用EMClient.getInstance().chatManager().loadAllConversations(),或者调用顺序不对,具体用法请参考登录章节。

//删除和某个user会话,如果需要保留聊天记录,传false
EMClient.getInstance().chatManager().deleteConversation(username, true);
//删除当前会话的某条聊天记录
EMConversation conversation = EMClient.getInstance().chatManager().getConversation(username);
conversation.removeMessage(deleteMsg.msgId);
List<EMMessage> messages = conversation.searchMsgFromDB(keywords, timeStamp, maxCount, from, EMConversation.EMSearchDirection.UP);

如果有从2.x SDK或者其他第三方SDK升级到目前3.x SDK的需要,可以使用下面的接口,构造EMMessage 对象,将历史消息导入到本地数据库中。

EMClient.getInstance().chatManager().importMessages(msgs);
//根据会话插入消息
EMConversation conversation = EMClient.getInstance().chatManager().getConversation(username);
conversation.insertMessage(message);

//直接插入消息
EMClient.getInstance().chatManager().saveMessage(message);

下载Demo及SDK

详细文档请参考 Java Doc


下一页:好友管理