创建聊天室、发送聊天室消息、邀请好友加入聊天室、获取服务器上所有聊天室、获取当前用户所在聊天室、退出聊天室、查询聊天室成员名称
效果图奉上:
创建聊天室
先跟大家介绍一下创建聊天室相关API:
- MultiUserChatManager:创建聊天室管理类,通过
getInstanceFor(XMPPConnection conn)
获取MultiUserChatManager
对象的实例,conn
这个参数就是当前的连接,可通过getConnection()
这个方法获取,之后会返回该MultiUserChatManager
对象。
/**
* 创建链接
*/
public XMPPTCPConnection getConnection() {
if (connection == null) {
//开线程打开连接,避免在主线程里面执行HTTP请求
new Thread(new Runnable() {
@Override
public void run() {
openConnection();
}
}).start();
}
return connection;
}
- MultiUserChat:MultiUserChat是在虚拟房间中的许多用户之间进行的对话。一个房间可能有许多具有不同隶属关系和角色的占用者。可能的隶属关系是“所有者”,“管理员”,“成员”和“被抛弃”。可能的角色是“主持人”,“参与者”和“访客”。每个角色和附属机构都保证不同的权限。以上来自官方文档释义。说白了就是创建一个房间,然后里面有不同的角色和现在的直播间类似。
下面看下如何获取该对象的实例:MultiUserChatManager.getMultiUserChat(EntityBareJid).
这里主要讲一下EntityBareJid
这个参数,也是我遇到一个比较大的坑。
它的构成是由:房间名称+聊天室域名+主机域名,这么说可能还有点模糊,我们看下代码roomName + "@conference."+getConnection().getServiceName())
组成的jid
,当然api里面需要传入的类型为EntityBareJid
类型,所以这里我们用JidCreate.entityBareFrom(roomName +"@conference."+getConnection().getServiceName()))
去创建,下面是创建MultiUserChat对象的代码:
/**
* 获取MultiUserChat 对象.
*
* @param roomName 聊天室名称
* @param subDomain 子域,默认为 conference
* @return {@link MultiUserChat}
*/
public MultiUserChat getMultiUserChat(String roomName, String subDomain) {
try {
return MultiUserChatManager.getInstanceFor(getConnection()).getMultiUserChat(
JidCreate.entityBareFrom(
roomName + "@" + (!subDomain.equals("") ?
subDomain : "conference") + "." +
getConnection().getServiceName()));
} catch (XmppStringprepException e) {
e.printStackTrace();
return null;
}
}
- 下面看下创建聊天室的方法:
/**
* 创建房间
* 类似于直播间的功能,用户只要离开房间或者下线就不在聊天室里显示.
*
* @param roomName 房间名称
* @param password 房间密码
*/
public MultiUserChat createMultiRoom(String roomName, String password) {
MultiUserChat muc;
try {
// 创建一个MultiUserChat
muc = getMultiUserChat(roomName, "");
// 创建聊天室
muc.create(Resourcepart.from(roomName));
// 获得聊天室的配置表单
Form form = muc.getConfigurationForm();
// 根据原始表单创建一个要提交的新表单。
Form submitForm = form.createAnswerForm();
// 向要提交的表单添加默认答复
for (FormField formField : form.getFields()) {
if (FormField.Type.hidden == formField.getType()
&& formField.getVariable() != null) {
// 设置默认值作为答复
submitForm.setDefaultAnswer(formField.getVariable());
}
}
// 设置聊天室的新拥有者
List<String> owners = new ArrayList<>();
owners.add(getConnection().getUser().asEntityBareJidString());// 用户JID
submitForm.setAnswer("muc#roomconfig_roomowners", owners);
// 设置聊天室是持久聊天室,即将要被保存下来
submitForm.setAnswer("muc#roomconfig_persistentroom", true);
// 房间仅对成员开放
submitForm.setAnswer("muc#roomconfig_membersonly", false);
// 允许占有者邀请其他人
submitForm.setAnswer("muc#roomconfig_allowinvites", true);
if (!password.equals("")) {
// 进入是否需要密码
submitForm.setAnswer("muc#roomconfig_passwordprotectedroom",
true);
// 设置进入密码
submitForm.setAnswer("muc#roomconfig_roomsecret", password);
}
// 能够发现占有者真实 JID 的角色
// submitForm.setAnswer("muc#roomconfig_whois", "anyone");
// 登录房间对话
submitForm.setAnswer("muc#roomconfig_enablelogging", true);
// 仅允许注册的昵称登录
submitForm.setAnswer("x-muc#roomconfig_reservednick", true);
// 允许使用者修改昵称
submitForm.setAnswer("x-muc#roomconfig_canchangenick", false);
// 允许用户注册房间
submitForm.setAnswer("x-muc#roomconfig_registration", false);
// 发送已完成的表单(有默认值)到服务器来配置聊天室
muc.sendConfigurationForm(submitForm);
} catch (XMPPException | XmppStringprepException | SmackException | InterruptedException e) {
e.printStackTrace();
return null;
}
return muc;
}
可以看到上面再创建的时候是需要有密码的,这就可以理解之前我说为什么它更新直播间一样的功能,你要在线那么就存在于该聊天室否则下线就会离开聊天室,只有你再次进入的时候才会存在于聊天室内。
发送聊天室消息
- 需要使用的方法:
MultiUserChat#sendMessage(String message)
非常简单吧,这样就可以将消息发送到聊天室内了。
邀请好友加入聊天室
- 需要使用的方法
MultiUserChat#invite(EntityBareJid user, String reason)或者MultiUserChat#invite(Message message, EntityBareJid user, String reason)
,下面跟大家解释下每个参数的含义
user:用户id 格式: jid@SERVICE_NAME
reason:邀请信息,可为null
message:用于发送邀请的消息
下面是邀请好友代码:
/**
* 邀请好友加入聊天室
*
* @param roomName 聊天室名称
* @param jid 用户id 格式: jid@SERVICE_NAME
* @param reason 邀请信息,可为null
* @return 邀请成功true ,邀请失败 false
*/
public boolean invite(String roomName, String jid, String reason) {
try {
getMultiUserChat(roomName, "")
.invite(JidCreate.entityBareFrom(jid + "@" + getConnection().getServiceName()), reason);
Log.e(TAG, " 已向 " + jid + " 用户发出邀请 成功...");
return true;
} catch (SmackException.NotConnectedException | InterruptedException | XmppStringprepException e) {
e.printStackTrace();
Log.e(TAG, " 已向 " + jid + " 用户发出邀请 失败...");
return false;
}
}
邀请好友就已经搞定,但仅仅是邀请,并不代表对方已同意 或 已加入聊天室,这里跟大家还得再介绍几个方法主要是用于监听接收用户邀请信息的监听以及如何加入聊天室。
- 邀请好友监听器:
InvitationListener
代码:
/**
* 添加侦听器以添加邀请通知。 收到邀请后,听众将被解雇。
* 通知{@link FriendListFragment} 刷新页面,最终是否添加由 {@link UnRoomsListActivity} 决定
*
* @param xmppConnection 收到邀请的XMPPConnection。
* @param room 邀请聊天室的房间。
* @param inviter 发送邀请的邀请者jid。
* @param reason 邀请者发出邀请的原因。
* @param password 加入聊天室时使用的密码。
* @param message 邀请者用来发送邀请的消息。
* @param invitation 收到的原始邀请信息。
*/
@Override
public void invitationReceived(XMPPConnection xmppConnection, MultiUserChat room, EntityJid inviter,
String reason, String password, Message message, MUCUser.Invite invitation) {
/*
* 1:获取该聊天室成员列表,查看当前用户是否在聊天室中,如果已经存在就不在调用加入聊天室方法
*/
XmppConnectionHelper connectionHelper = XmppConnectionHelper.getInstance();
//当前用户jid,只要用户名称,去掉了domain 和 id
String currentUserJid = Utils.jidToString(connectionHelper.getUser().toString());
//获取聊天室内的用户
List<String> multiUserRooms = connectionHelper.findMultiUserRooms(connectionHelper.getMultiUserChat(room.getRoom()));
if (multiUserRooms.size() > 0) {//用户已经加入过聊天室,所以需要判断此用户是否已经存在于聊天室内
for (String user : multiUserRooms) {
//只要用户名称,去掉聊天室domain
user = user.substring(user.lastIndexOf("/") + 1);
Log.d(TAG, "roomsPersonnel name :" + user);
if (currentUserJid.equals(user)) {//当前用户存在于聊天室内,直接结束循环
return;
} else {//当前用户不在聊天室
//邀请好友加入聊天室
notifyInvite(room, inviter, reason, password, message);
}
}
} else {//此时用户没有任何聊天室
//邀请好友加入聊天室
notifyInvite(room, inviter, reason, password, message);
}
}
/**
* 接收聊天室邀请,将数据设置给{@link InvitationObservable#setInviteEntity(InviteEntity)},并通知{@link InvitationObserver#update(Observable, Object)}
*
* @param room 邀请聊天室的房间。
* @param inviter 发送邀请的邀请者jid。
* @param reason 邀请者发出邀请的原因。
* @param password 加入聊天室时使用的密码。
* @param message 邀请者用来发送邀请的消息。
*/
private void notifyInvite(MultiUserChat room, EntityJid inviter, String reason, String password, Message message) {
count++;
Log.e(TAG, "收到来自 " + inviter + " 的 " + room.getRoom().asEntityBareJidString() + " 聊天室邀请。邀请附带内容:" + reason + " 数量: " + count);
InviteEntity entity = new InviteEntity(count, room.getRoom().asEntityBareJidString(),
inviter.asEntityBareJidString(), reason, password, message.getBody());
//将数据设置给被观察者
observable.setInviteEntity(entity);
//添加会议室消息监听器
room.addMessageListener(new XMChatMessageListener());
}
核心代码已经贴出来了,现在看下最后一行,添加会议室消息监听器:这里也是非常重要的,因为如果你不添加消息监听器那么无论是收发消息都无法成功。下面给大家贴下关于会议室消息监听器的代码:
/**
* @author Hexl
* @desc 信息接收监听器
* IncomingChatMessageListener 单聊消息接收监听
* OutgoingChatMessageListener 单聊消息发送监听
* MessageListener 群聊消息监听
* @date 2018/10/19
*/
public class XMChatMessageListener implements IncomingChatMessageListener, OutgoingChatMessageListener, MessageListener {
public static ChatObservable observable = new ChatObservable();
public static void addObserver(Observer observer) {
observable.addObserver(observer);
}
public static void removeObserver(Observer observer) {
observable.deleteObserver(observer);
}
/**
* 单聊消息接收
*
* @param entityBareJid
* @param message
* @param chat
*/
@Override
public void newIncomingMessage(EntityBareJid entityBareJid, Message message, Chat chat) {
dealWithNotify(message, ConversationEntity.RECEIVE);
}
/**
* 单聊消息发送
*
* @param entityBareJid
* @param message
* @param chat
*/
@Override
public void newOutgoingMessage(EntityBareJid entityBareJid, Message message, Chat chat) {
dealWithNotify(message, ConversationEntity.SEND);
}
/**
* 群消息接收
*
* @param message
*/
@Override
public void processMessage(Message message) {
Log.e("XMChatMessageListener", "message.toXML():" + message.toXML());
if (message.getType() == Message.Type.groupchat) {
// TODO: 2018/11/6 暂时以这种形式区分状态,这样是不准确的,
List<ExtensionElement> extensions = message.getExtensions();
if (extensions.size() > 0) {
dealWithNotify(message, ConversationEntity.RECEIVE);
}else {
dealWithNotify(message, ConversationEntity.SEND);
}
}
}
private void dealWithNotify(Message message, int msgType) {
//1.将消息体保存到db消息表
//2.生成会话列表并且保存于会话列表
//3.通知"会话列表页面" 和 "当前聊天页面" 消息来了
observable.setMessage(message, msgType);
}
}
当然这也还不算完全成功,因为我们还没有真正的将用户加入到聊天室,以上都属于一些config,下面才是真正将用户加入到聊天室内的代码:
/**
* 加入聊天室(只能将当前用户加入到指定的聊天室中)
* <pre>
* <code>
* XmppConnectionHelper.getInstance().joinMultiUserChat("1","密码", "聊天室名称");
* </code>
* </pre>
*
* @param user 昵称
* @param roomsName 聊天室名
*/
public MultiUserChat joinMultiUserChatRooms(String user, String password, String roomsName) {
// 使用XMPPConnection创建一个MultiUserChat窗口
MultiUserChat muc = getMultiUserChat(roomsName, "");
try {
// 用户加入聊天室
muc.join(muc
.getEnterConfigurationBuilder(Resourcepart.from(user))
.withPassword(password)
.requestMaxCharsHistory(99)//只获取最后99条信息
.build());
Log.e(TAG, "用户: " + muc.getNickname().toString() + " 加入" + roomsName + "聊天室加入成功........");
return muc;
} catch (XMPPException | XmppStringprepException | InterruptedException | SmackException e) {
Log.e(TAG, "用户: " + muc.getNickname().toString() + " 加入" + roomsName + "聊天室加入失败........");
e.printStackTrace();
return null;
}
}
获取服务器上所有聊天室
/**
* 获取所有聊天室列表,包括未加入的聊天室
*/
public List<HostedRoom> getHostRooms() {
try {
return getMultiUserChatManager()
//此处在拼接聊天室 host 时必须添加 "conference." 不然会引发NotAMucServiceException异常,因为聊天室的 host 为conference.+ SERVICE_NAME
.getHostedRooms(JidCreate.domainBareFrom("conference." + getConnection().getServiceName()));
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException |
InterruptedException | SmackException.NotConnectedException |
XmppStringprepException | MultiUserChatException.NotAMucServiceException e) {
e.printStackTrace();
}
return null;
}
获取当前用户所在聊天室
/**
* 获取当前用户所在的聊天室列表
*
* @return 所在聊天室 jid
*/
public List<EntityBareJid> getMultiUserHostRooms() {
Set<EntityBareJid> setJoinedRooms = getMultiUserChatManager().getJoinedRooms();
return new ArrayList<>(setJoinedRooms);
}
退出聊天室
/**
* 退出聊天室
*
* @param roomName 聊天室名称
*/
public void quitRooms(String roomName) {
try {
//退出群
getMultiUserChat(roomName, "").leave();
} catch (SmackException.NotConnectedException | InterruptedException e) {
e.printStackTrace();
}
}
查询聊天室成员名称
/**
* 查询聊天室成员名字
* 只有当前用户已经被加入到聊天室后才能查询到该聊天室成员,否则为null
*
* @param muc
* @return xxx(房间名称)@conference.openfire.im.hexl/aaaa(昵称)
*/
public List<String> findMultiUserRooms(MultiUserChat muc) {
List<String> listUser = new ArrayList<>();
List<EntityFullJid> it = muc.getOccupants();
// 遍历出聊天室人员名称
for (EntityFullJid entityFullJid : it) {
// 聊天室成员名字
listUser.add(entityFullJid.toString());
}
return listUser;
}