主页 > 开发文档 > Android之XMPP多人聊天篇(Asmack + Openfire)

Android之XMPP多人聊天篇(Asmack + Openfire)

 上一篇中提到了一点的多人聊天MultiUserChat,由于Openfire只支持临时群租的聊天,也就是临时会议室;所以要实现多人聊天这里有两种折中的办法:
. 自定义Openfire插件,就是自己写个Openfire集群的插件,从Openfire端建立组Table与人Table的联系,或者说对应关系(推荐)
. 通过后台服务器,辅助建立讨论组与成员之间的关系,以加入临时会议室的形式来实现群组关系;想也知道,这种方法相对于上种需要多加一些逻辑,   所以资源消耗稍多一些,建议使用上种。
 
一、多人聊天MultiUserChat篇
 
1、首先是创建讨论组,以默认方式创建这里不说了,不实用。写法固定,注释比较全,这里我把加入的密码默置为password,各人可以自行修改
      public boolean creConferenceRoom(String roomName) {
 
if (mConnection == null || !mConnection.isConnected()) {
System.out.println("连接异常...");
openXmppConnection();
return false;
}
/**
 * 判断是否存在同名讨论组
 */
if (isRoomExists(roomName)) {
System.out.println("创建聊天组存在同名组");
return false;
}
MultiUserChat mMultiUserChat = null;
String roomService = roomName + "@conference."
+ mConnection.getServiceName();// 房间域名
 
try {
mMultiUserChat = new MultiUserChat(mConnection, roomService);
// 创建聊天室
mMultiUserChat.create(roomName);
 
/** 聊天室配置表单 */
 
Form form = mMultiUserChat.getConfigurationForm(); // 获取配置表单
Form submitForm = form.createAnswerForm();// 生成提交表单
 
// 向提交表单添加默认答复
for (Iterator<FormField> fields = form.getFields(); fields
.hasNext();) {
 
FormField mFormField = (FormField) fields.next();
if (!FormField.TYPE_HIDDEN.equals(mFormField.getType())
&& mFormField.getVariable() != null) {
submitForm.setDefaultAnswer(mFormField.getVariable()); // 设置默认值作为答复
}
}
 
// 设置当前用户为聊天室的创建者
List<String> roomOwner = new ArrayList<String>();
roomOwner.add(mConnection.getUser());
submitForm.setAnswer("muc#roomconfig_roomowners", roomOwner);
 
submitForm.setAnswer("muc#roomconfig_persistentroom", true); // 持久聊天室
submitForm.setAnswer("muc#roomconfig_membersonly", false); // 仅对成员开放
submitForm.setAnswer("muc#roomconfig_allowinvites", true); // 允许邀请其他人
 
submitForm.setAnswer("muc#roomconfig_passwordprotectedroom", true);
submitForm.setAnswer("muc#roomconfig_roomsecret", "password");
 
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); // 允许用户注册房间
mMultiUserChat.sendConfigurationForm(submitForm); // 提交配置表单
} catch (XMPPException e) {
e.printStackTrace();
return false;
}
return true;
}
2、获取Openfire上所有的讨论组,是所有的组,和人没有关系,不太实用。
      public List<ConferenceRoom> getConferenceRooms() {
 
if (mConnection == null || !mConnection.isConnected()) {
return null;
}
List<ConferenceRoom> roomInfo = new ArrayList<ConferenceRoom>();
Collection<HostedRoom> mConferenceRooms;
String mJID = "conference." + mConnection.getServiceName();
System.out.println("JID=" + mJID);
 
/**
 * MultiUserChat.getHostedRooms(conn,JID)
 * 参数JID如果为conn.getServiceName得到的是本服务器下所有类型的会议室,种类如
 * conference/search/proxy/pubsub后面分别加上".063-qichunjie"
 * conference.063-qichunjie类型的会议室才是我们创建的会议室
 */
try {
mConferenceRooms = MultiUserChat.getHostedRooms(mConnection, mJID);
if (!mConferenceRooms.isEmpty()) {
for (HostedRoom room : mConferenceRooms) {
 
RoomInfo mRoomInfo = MultiUserChat.getRoomInfo(mConnection,
room.getJid());
System.out.println("+++++++++++++++++++++++++++");
System.out.println(room.getName());
ConferenceRoom cr = new ConferenceRoom();
cr.setRoomJID(room.getJid());
cr.setRoomName(room.getName());
cr.setDescription(mRoomInfo.getDescription());
cr.setOccupantsCount(mRoomInfo.getOccupantsCount());
roomInfo.add(cr);
}
return roomInfo;
}
 
} catch (XMPPException e) {
e.printStackTrace();
}
return null;
}
3、获取指定讨论组的成员,传入参数讨论组名;这里获取到的成员必须当前在线且加入讨论组;因为不支持固定群组的关系,所以不可能获取到离线的成员,因为一旦离线意味着退出该讨论组。
      public List<String> getMultiUsers(String roomName) {
 
List<String> mUserList = new ArrayList<String>();
// 指定获取哪个会议室的成员
MultiUserChat mUserChat = new MultiUserChat(mConnection, roomName
+ "@conference." + mConnection.getServiceName());
// 获取成员列表
Iterator<String> user = mUserChat.getOccupants();
// 读取列表成员
while (user.hasNext()) {
String userName = StringUtils.parseResource(user.next());
// System.out.println("会议室" + roomName + "列表成员--------" + userName);
mUserList.add(userName);
}
return mUserList;
}
4、指定成员加入到指定讨论组
      public MultiUserChat joinConferenceRoom(String hostName, String roomName,
String password) {
 
if (mConnection == null || !mConnection.isConnected()) {
return null;
}
 
if (!isRoomExists(roomName)) {
return null;
}
 
try {
// 创建一个MultiUserChat,用来指定要加入的Room
MultiUserChat mMultiUserChat = new MultiUserChat(mConnection,
roomName + "@conference." + mConnection.getServiceName());
 
// 设置新加入成员要接受的服务器聊天室的聊天记录数
DiscussionHistory history = new DiscussionHistory();
history.setMaxStanzas(10);
 
// 用户加入聊天室
mMultiUserChat.join(hostName, password, history,
SmackConfiguration.getPacketReplyTimeout());
return mMultiUserChat;
} catch (XMPPException e) {
e.printStackTrace();
}
return null;
}
5、通过MultiUserChat发送群组消息,getvalideMultiChat()方法作用是判断当前是否连接,如果不连接那么一直连接上服务器再新建MultiUserChat,返回一个有效的MultiUserChat对象来通话。
     String roomJID = Constants.CURRENT_ROOM + "@conference." + mXmpp.getConnection().getServiceName();
MultiUserChat muc = GroupManager.getChat(roomJID);
 
if (!messageInput.getText().toString().equals("")) {
MultiUserChat chat = getvalideMultiChat(muc);
try {
chat.sendMessage(messageInput.getText().toString());
} catch (XMPPException e) {
e.printStackTrace();
}
6、群组消息的监听   群组消息的监听最好设置在登陆之后,在单独的Service中进行。我这里是采用后台服务器的方式来实现;具体流程:登陆--->跳转到群组Service--->获取当前登陆的我的所有群组--->加入每个群组然后设置群消息监听。首先加入群组上面已经说了,这里再放下代码吧
 
      //Group是存放群组类的List
      MultiUserChat chat = new MultiUserChat(conn,group.getRoomName() + "@conference."+ conn.getServiceName());
      String mUser = conn.getUser(); //通过XMPP连接获取的当前登陆用户,具体格式 用户名 @ ServiceName / 群组名
      chat.join(mUser.split("/")[0].split("@")[0],"password");
 
        如何来添加群消息的监听?
        //chat为上面的MultiUserChat
        chat.addMessageListener(new OnGroupChatListener(chat));
 
        接收群消息的监听类,自己发送的消息也在此处接收一块处理,代码如下:
        private class OnGroupChatListener implements PacketListener {
private MultiUserChat mChat = null;
 
public OnGroupChatListener(MultiUserChat chat) {
mChat = chat;
}
 
@Override
public void processPacket(Packet packet) {
Message msg = (Message) packet;
DelayInformation inf = (DelayInformation) msg.getExtension("x",
"jabber:x:delay");
if (msg.getFrom().equals(mChat.getRoom())) {
return;
}
if (msg.getBody() != null) {
if (inf == null) { // 新消息
 
System.out.println("--------------群组消息------------");
System.out.println("来自:" + msg.getFrom());
System.out.println("消息:" + msg.getBody());
}else {
                         //旧消息,该房间的所有消息
                        }
}
7、邀请    无论是你新建讨论组邀请别人还是别人新建组邀请你,我们要知道如何发送邀请以及如何监听别人的邀请。一般如果别人邀请我们加入讨论组,我们可能需要更新下这个新的讨论组,邀请的监听可以作为一个请求并更新的信号。
(1)发送邀请
         MultiUserChat chat = new MultiUserChat(conn,group.getRoomName() + "@conference." + conn.getServiceName());
         chat.invite(user.getAccount() + "@netstars",mUser.split("@")[0] + "邀请你加入" + group.getRoomName() + "房间");
 
(2)邀请监听,conn是XMPPConnection
         MultiUserChat.addInvitationListener(conn, new InvitationListener() {
 
@Override
public void invitationReceived(Connection arg0, String arg1,
String arg2, String arg3, String arg4, Message arg5) {
 
System.out.println("收到来自" + arg2 + "的聊天室邀请,加入聊天室【" + arg1
+ "】");
}
});
 
最后再强调一下:
(1)这里用的将群组与成员的关系保存后台服务器,开始需要根据登陆的账户获取我的讨论组
(2)所有的监听,群消息、单人消息、邀请消息以及连接监听等,切勿重复设置;且最好在关闭连接以及登出的同时remove掉。
(3)Asmack + Openfire框架确实稳定性不够,使用须慎之又慎;如果使用,请使用 Openfire版本不超过3.7.0版本
 
以上是关于群组聊天的一些方法,希望可以给需要的人带来一些帮助... ...