Android中基于XMPP协议实现IM聊天程序与多人聊天室
简单的im聊天程序
由于项目需要做一个基于xmpp协议的android通讯软件。故开始研究xmpp。
xmpp协议采用的是客户端-服务器架构,所有从一个客户端发到另一个客户端的消息和数据都必须经过xmpp服务器转发,而且支持服务器间dns的路由,也就是说可以构建服务器集群,使不同的
服务器下的客户端也可以通信,xmpp的前身是一个开源组织制定的网络通信协议——jabber,xmpp的核心是在网络上分片段发送xml流的协议,这个协议是xmpp的即时通讯指令的传递手段。
为了防止服务器间发送的数据被篡改或偷听,xmpp服务器通信引入了tls机制,使用tls机制能实现数据的加密,从而保证了在数据传输过程种数据的安全。
一个xmpp实体的地址称为jabber identifier或jid,作用类似于ip地址。一个合法的jid包括节点名,域名资源名,其格式为:jid=[node'@']domain['/'resource]
xmpp协议的命名空间:
- jabber:iq:private -- 私有数据存储,用于本地用户私人设置信息,比如用户备注等。
- jabber:iq:conference -- 一般会议,用于多个用户之间的信息共享
- jabber:x:encrypted -- 加密的消息,用于发送加密消息
- jabber:x:expire -- 消息终止
- jabber:iq:time -- 客户端时间
- jabber:iq:auth -- 简单用户认证,一般用于服务器之间或者服务器和客户端之间的认证
- jabber:x:roster -- 内部花名册
- jabber:x:signed -- 标记的在线状态
- jabber:iq:search -- 用户数据库查询,用于向服务器发送查询请求
- jabber:iq:register -- 注册请求,用于用户注册相关信息
- jabber:x:iq:roster -- 花名册管理
- jabber:x:conference -- 会议邀请,用于向参加会议用户发送开会通知
- jabber:x:event -- 消息事件
- vcard-temp -- 临时的vcard,用于设置用户的头像以及昵称等
在网上找了下,有开源的项目beem,开源的用于android的xmpp框架asmack,asmack是smack的android版本。现在开始学习smack
。xmpp就是神马东西,就不废话了。首先在网上下一个openfire和spack,不知道这两个是什么东西,就直接google吧。安装openfire需要mysql的支持,当然,oracle,sqlserver肯定是可以的。还是先上图吧:
openfire + spark + myxmppp
import java.io.inputstreamreader; import java.util.collection; import org.jivesoftware.smack.chat; import org.jivesoftware.smack.chatmanager; import org.jivesoftware.smack.chatmanagerlistener; import org.jivesoftware.smack.connectionconfiguration; import org.jivesoftware.smack.messagelistener; import org.jivesoftware.smack.privacylistmanager; import org.jivesoftware.smack.roster; import org.jivesoftware.smack.rosterentry; import org.jivesoftware.smack.rostergroup; import org.jivesoftware.smack.rosterlistener; import org.jivesoftware.smack.xmppconnection; import org.jivesoftware.smack.packet.message; import org.jivesoftware.smack.packet.presence; public class testsmack { public static void main(string[] args) {xmppconnection.debug_enabled = true; //我的电脑ip:10.16.25.90 final connectionconfiguration connectionconfig = new connectionconfiguration("10.16.25.91", 5222, ""); connectionconfig.setsaslauthenticationenabled(false); try { xmppconnection connection = new xmppconnection(connectionconfig); connection.connect();//连接 connection.login("test", "test");//登陆 system.out.println(connection.getuser()); chatmanager chatmanager = connection.getchatmanager(); //新建一个会话 chat newchat = chatmanager.createchat("test3@pc2010102716", new messagelistener() { public void processmessage(chat chat, message message) { system.out.println("received from 【" + message.getfrom() + "】 message: " + message.getbody()); } }); // 监听被动接收消息,或广播消息监听器 chatmanager.addchatlistener(new chatmanagerlistener() { @override public void chatcreated(chat chat, boolean createdlocally) { chat.addmessagelistener(new messagelistener() { @override public void processmessage(chat chat, message message) { system.out.println("received from 【" + message.getfrom() + "】 message: " + message.getbody()); } }); } }); //发送消息 newchat.sendmessage("我是菜鸟"); //获取花名册 roster roster = connection.getroster(); collection<rosterentry> entries = roster.getentries(); for(rosterentry entry : entries) { system.out.print(entry.getname() + " - " + entry.getuser() + " - " + entry.gettype() + " - " + entry.getgroups().size()); presence presence = roster.getpresence(entry.getuser()); system.out.println(" - " + presence.getstatus() +" - "+ presence.getfrom()); } //添加花名册监听器,监听好友状态的改变。 roster.addrosterlistener(new rosterlistener() { @override public void entriesadded(collection<string> addresses) { system.out.println("entriesadded"); } @override public void entriesupdated(collection<string> addresses) { system.out.println("entriesupdated"); } @override public void entriesdeleted(collection<string> addresses) { system.out.println("entriesdeleted"); } @override public void presencechanged(presence presence) { system.out.println("presencechanged - >" + presence.getstatus()); } }); //创建组 // /rostergroup group = roster.creategroup("大学"); // for(rosterentry entry : entries) { // group.addentry(entry); // } for(rostergroup g : roster.getgroups()) { for(rosterentry entry : g.getentries()) { system.out.println("group " +g.getname() +" >> " + entry.getname() + " - " + entry.getuser() + " - " + entry.gettype() + " - " + entry.getgroups().size()); } } //发送消息 bufferedreader cmdin = new bufferedreader(new inputstreamreader(system.in)); while(true) { try { string cmd = cmdin.readline(); if("!q".equalsignorecase(cmd)) { break; } newchat.sendmessage(cmd); }catch(exception ex) { } } connection.disconnect(); system.exit(0); } catch (exception e) { e.printstacktrace(); } } }
以上代码如果在一般的java project上运行需要加入smack.jar 和klmx2.jar,如果是android project,基本代码不需改变只需将其放入oncreate(...)方法下即可,需要加入asmack.jar包.
1、connectionconfiguration
作为用于与xmpp服务建立连接的配置。它能配置;连接是否使用tls,sasl加密。
包含内嵌类:connectionconfiguration.securitymode
2、xmppconnection.
xmppconnection这个类用来连接xmpp服务.
可以使用connect()方法建立与服务器的连接。disconnect()方法断开与服务器的连接.
在创建连接前可以使用xmppconnection.debug_enabled = true; 使开发过程中可以弹出一个gui窗口,用于显示我们的连接与发送packet的信息。
3、chatmanager
用于监控当前所有chat。可以使用createchat(string userjid, messagelistener listener)创建一个聊天。
4、chat
chat用于监控两个用户间的一系列message。使用addmessagelistener(messagelistener listener)当有任何消息到达时将会触发listener的processmessage(chat chat, message message)
方法.
我们可以使用sendmessage()发送消息,这个方法有两个重载方法,一种类类型的参数时string类型,另一种则是传入message对象(后面介绍)。
那么有这样一种情况,当别人主动跟我们建立连接发送消息,或者系统发送消息时我们怎么才能接收消息呢?
我现在是这样操作的:
chatmanager.addchatlistener(new chatmanagerlistener() { @override public void chatcreated(chat chat, boolean createdlocally) { chat.addmessagelistener(new messagelistener() { @override public void processmessage(chat chat, message message) { system.out.println("received message: " + message.getbody()); } }); } });
5、message
- message用于表示一个消息包(可以用调试工具看到发送包和接收包的具体内容)。它有以下多种类型。
- message.type.normal -- (默认)文本消息(比如邮件)
- message.type.chat -- 典型的短消息,如qq聊天的一行一行显示的消息
- message.type.group_chat -- 群聊消息
- message.type.headline -- 滚动显示的消息
- message.type.error -- 错误的消息
- message有两个内部类:
- message.body -- 表示消息体
- message.type -- 表示消息类型
6、roster
表示存储了很多rosterentry的一个花名册.为了易于管理,花名册的项被分贝到了各个group中.
当建立与xmpp服务的连接后可以使用connection.getroster()获取roster对象。
别的用户可以使用一个订阅请求(相当于qq加好友)尝试订阅目的用户。可以使用枚举类型roster.subscriptionmode的值处理这些请求:
accept_all: 接收所有订阅请求
reject_all:拒绝所有订阅请求
manual: 手工处理订阅请求
创建组:rostergroup group = roster.creategroup("大学");
向组中添加rosterentry对象: group.addentry(entry);
7、rosterentry
表示roster(花名册)中的每条记录.它包含了用户的jid,用户名,或用户分配的昵称.
8、rostergroup
表示rosterentry的组。可以使用addentry(rosterentry entry)添加。contains(string user) 判断某用户是否在组中.当然removeentry(rosterentry entry)就是从组中移除了。getentries()
获取所有rosterentry.
9、presence
表示xmpp状态的packet。每个presence packet都有一个状态。用枚举类型presence.type的值表示:
available -- (默认)用户空闲状态
unavailable -- 用户没空看消息
subscribe -- 请求订阅别人,即请求加对方为好友
subscribed -- 统一被别人订阅,也就是确认被对方加为好友
unsubscribe -- 他取消订阅别人,请求删除某好友
unsubscribed -- 拒绝被别人订阅,即拒绝对放的添加请求
error -- 当前状态packet有错误
内嵌两个枚举类型:presence.mode和presence.type.
可以使用setstatus自定义用户当前的状态(像qq一样的)
multiuserchat聊天室
import java.io.bufferedreader; import java.io.inputstreamreader; import java.util.arraylist; import java.util.collection; import java.util.iterator; import java.util.list; import org.jivesoftware.smack.chat; import org.jivesoftware.smack.connectionconfiguration; import org.jivesoftware.smack.messagelistener; import org.jivesoftware.smack.packetlistener; import org.jivesoftware.smack.smackconfiguration; import org.jivesoftware.smack.xmppconnection; import org.jivesoftware.smack.xmppexception; import org.jivesoftware.smack.packet.message; import org.jivesoftware.smack.packet.packet; import org.jivesoftware.smack.provider.providermanager; import org.jivesoftware.smackx.form; import org.jivesoftware.smackx.formfield; import org.jivesoftware.smackx.servicediscoverymanager; import org.jivesoftware.smackx.muc.defaultparticipantstatuslistener; import org.jivesoftware.smackx.muc.defaultuserstatuslistener; import org.jivesoftware.smackx.muc.discussionhistory; import org.jivesoftware.smackx.muc.hostedroom; import org.jivesoftware.smackx.muc.invitationlistener; import org.jivesoftware.smackx.muc.invitationrejectionlistener; import org.jivesoftware.smackx.muc.multiuserchat; import org.jivesoftware.smackx.muc.roominfo; import org.jivesoftware.smackx.muc.subjectupdatedlistener; import org.jivesoftware.smackx.packet.chatstateextension; import org.jivesoftware.smackx.packet.discoverinfo; import org.jivesoftware.smackx.packet.discoveritems; import org.jivesoftware.smackx.packet.offlinemessageinfo; import org.jivesoftware.smackx.packet.offlinemessagerequest; import org.jivesoftware.smackx.provider.adhoccommanddataprovider; import org.jivesoftware.smackx.provider.bytestreamsprovider; import org.jivesoftware.smackx.provider.dataformprovider; import org.jivesoftware.smackx.provider.discoverinfoprovider; import org.jivesoftware.smackx.provider.discoveritemsprovider; import org.jivesoftware.smackx.provider.ibbproviders; import org.jivesoftware.smackx.provider.mucadminprovider; import org.jivesoftware.smackx.provider.mucownerprovider; import org.jivesoftware.smackx.provider.mucuserprovider; import org.jivesoftware.smackx.provider.streaminitiationprovider; import org.jivesoftware.smackx.provider.vcardprovider; import org.jivesoftware.smackx.provider.xhtmlextensionprovider; public class testsmack2 { public static void main(string[] args) {xmppconnection.debug_enabled = true; final connectionconfiguration connectionconfig = new connectionconfiguration("pc2010102716", 5222, ""); connectionconfig.setsaslauthenticationenabled(false); providermanager pm = providermanager.getinstance(); configure(pm); try { xmppconnection connection = new xmppconnection(connectionconfig); connection.connect();//连接 initfeatures(connection); connection.login("test", "test");//登陆 //聊天室 //multiuserchat multiuserchat = new multiuserchat(connection, new invitationlistener() {}); //查找服务 system.out.println(connection.getservicename()); list<string> col = getconferenceservices(connection.getservicename(), connection); for (object acol : col) { string service = (string) acol; //查询服务器上的聊天室 collection<hostedroom> rooms = multiuserchat.gethostedrooms(connection, service); for(hostedroom room : rooms) { //查看room消息 system.out.println(room.getname() + " - " +room.getjid()); roominfo roominfo = multiuserchat.getroominfo(connection, room.getjid()); if(roominfo != null) { system.out.println(roominfo.getoccupantscount() + " : " + roominfo.getsubject()); } } } /*---创建默认配置的聊天室 --- 先看看官方的文档: creates a new multi user chat with the specified connection and room name. note: no * information is sent to or received from the server until you attempt to * {@link #join(string) join} the chat room. on some server implementations, * the room will not be created until the first person joins it * 最重要一句:直到用户调用join方法的时候聊天室才会被创建 */ multiuserchat muc = new multiuserchat(connection, "instant@conference.pc2010102716"); muc.create("user1"); muc.sendconfigurationform(new form(form.type_submit)); //----创建手动配置聊天室---- muc = new multiuserchat(connection, "reserved4@conference.pc2010102716"); //销毁聊天室 //muc.destroy("test", null); muc.create("user2"); //获取聊天室的配置表单 form form = muc.getconfigurationform(); //根据原始表单创建一个要提交的新表单 form submitform = form.createanswerform(); //向提交的表单添加默认答复 for(iterator<formfield> fields = form.getfields(); fields.hasnext();) { formfield field = (formfield) fields.next(); if(!formfield.type_hidden.equals(field.gettype()) && field.getvariable() != null) { submitform.setdefaultanswer(field.getvariable()); } } //重新设置聊天室名称 submitform.setanswer("muc#roomconfig_roomname", "reserved4 room"); //设置聊天室的新拥有者 list<string> owners = new arraylist<string>(); owners.add("test@pc2010102716"); submitform.setanswer("muc#roomconfig_roomowners", owners); //设置密码 submitform.setanswer("muc#roomconfig_passwordprotectedroom", true); submitform.setanswer("muc#roomconfig_roomsecret", "reserved"); //设置描述 submitform.setanswer("muc#roomconfig_roomdesc", "新创建的reserved聊天室"); //设置聊天室是持久聊天室,即将要被保存下来 //submitform.setanswer("muc#roomconfig_persistentroom", true); //发送已完成的表单到服务器配置聊天室 muc.sendconfigurationform(submitform); //加入聊天室(使用昵称喝醉的毛毛虫 ,使用密码ddd) muc = new multiuserchat(connection, "ddd@conference.pc2010102716"); muc.join("喝醉的毛毛虫", "ddd"); //监听消息 muc.addmessagelistener(new packetlistener() { @override public void processpacket(packet packet) { message message = (message) packet; system.out.println(message.getfrom() + " : " + message.getbody());; } }); //muc = new multiuserchat(connection, "ddd@conference.pc2010102716"); //muc.join("喝醉的毛毛虫", "ddd"); //加入聊天室(使用昵称喝醉的毛毛虫 ,使用密码ddd)并且获取聊天室里最后5条信息, //注:addmessagelistener监听器必须在此join方法之前,否则无法监听到需要的5条消息 muc = new multiuserchat(connection, "ddd@conference.pc2010102716"); discussionhistory history = new discussionhistory(); history.setmaxstanzas(5); muc.join("喝醉的毛毛虫", "ddd", history, smackconfiguration.getpacketreplytimeout()); //监听拒绝加入聊天室的用户 muc.addinvitationrejectionlistener(new invitationrejectionlistener() { @override public void invitationdeclined(string invitee, string reason) { system.out.println(invitee + " reject invitation, reason is " + reason); } }); //邀请用户加入聊天室 muc.invite("test3@pc2010102716", "大家来谈谈人生"); //监听邀请加入聊天室请求 multiuserchat.addinvitationlistener(connection, new invitationlistener() { @override public void invitationreceived(xmppconnection conn, string room, string inviter, string reason, string password, message message) { //例:直接拒绝邀请 multiuserchat.decline(conn, room, inviter, "你丫很闲啊!"); } }); //根据roomjid获取聊天室信息 roominfo roominfo = multiuserchat.getroominfo(connection, "ddd@conference.pc2010102716"); system.out.println(roominfo.getroom() + "-" + roominfo.getsubject() + "-" + roominfo.getoccupantscount()); //判断用户是否支持multi-user聊天协议 //注:需要加上资源标识符 boolean supports = multiuserchat.isserviceenabled(connection, "test3@pc2010102716/spark"); //获取某用户所加入的聊天室 if(supports) { iterator<string> joinedrooms = multiuserchat.getjoinedrooms(connection, "test3@pc2010102716/spark"); while(joinedrooms.hasnext()) { system.out.println("test3 has joined room " + joinedrooms.next()); } } //与聊天室用户私聊 chat chat = muc.createprivatechat("ddd@conference.pc2010102716/飞鸟", new messagelistener() { @override public void processmessage(chat chat, message message) { system.out.println("private chat: received message from " + message.getfrom() + "-" + message.getbody()); } }); chat.sendmessage("今天不用加班吧?"); //改变聊天室主题 muc.addsubjectupdatedlistener(new subjectupdatedlistener() { @override public void subjectupdated(string subject, string from) { system.out.println("subject updated to " + subject +" by " + from); } }); //muc.changesubject("new subject11"); /*一个成员可能有四种角色: 1:主持者(moderator) (权限最大的角色,管理其他成员在聊天室中的角色 2:参与者(participant 3:游客 (visitor) (不能向所有成员发送消息) 4:无(没有角色)(none) */ /*聊天室用户可以有5种从属关系 * 1、所有者 owner * 2、管理员 admin * 3、成员 member * 4、被驱逐者 outcast * 5、无(不存在从属关系) none */ //配置聊天室为moderated聊天室 form = muc.getconfigurationform(); form answerform = form.createanswerform(); answerform.setanswer("muc#roomconfig_moderatedroom", "1"); muc.sendconfigurationform(answerform); //监听自己的状态变更和事件 muc.adduserstatuslistener(new defaultuserstatuslistener() { @override public void voicerevoked() { super.voicerevoked(); system.out.println("你被禁言了!"); } @override public void voicegranted() { super.voicegranted(); system.out.println("你被批准发言了!"); } @override public void membershipgranted() { super.membershipgranted(); system.out.println("你被赋予了member权限"); } @override public void membershiprevoked() { super.membershiprevoked(); system.out.println("你被解除了member权限"); } @override public void admingranted() { super.admingranted(); system.out.println("你被赋予了管理员权限"); } @override public void adminrevoked() { super.adminrevoked(); system.out.println("你被解除了管理员权限"); } //...... }); //房主(owner)批准test3发言权 muc.grantvoice("test3@pc2010102716"); //监听他人状态变更 muc.addparticipantstatuslistener(new defaultparticipantstatuslistener() { @override public void voicegranted(string participant) { super.voicegranted(participant); system.out.println(participant + "被批准发言了!"); } @override public void voicerevoked(string participant) { super.voicerevoked(participant); system.out.println(participant + "被禁言了!"); } @override public void membershiprevoked(string participant) { super.membershiprevoked(participant); } @override public void admingranted(string participant) { super.admingranted(participant); } @override public void adminrevoked(string participant) { super.adminrevoked(participant); } }); //房主(owner)批准test3管理员特权 muc.grantadmin("test3@pc2010102716"); //发送消息 bufferedreader cmdin = new bufferedreader(new inputstreamreader(system.in)); while(true) { try { string cmd = cmdin.readline(); if("!q".equalsignorecase(cmd)) { break; } }catch(exception ex) { } } connection.disconnect(); system.exit(0); } catch (exception e) { e.printstacktrace(); } } public static list<string> getconferenceservices(string server, xmppconnection connection) throws exception { list<string> answer = new arraylist<string>(); servicediscoverymanager discomanager = servicediscoverymanager.getinstancefor(connection); discoveritems items = discomanager.discoveritems(server); for (iterator<discoveritems.item> it = items.getitems(); it.hasnext();) { discoveritems.item item = (discoveritems.item)it.next(); if (item.getentityid().startswith("conference") || item.getentityid().startswith("private")) { answer.add(item.getentityid()); } else { try { discoverinfo info = discomanager.discoverinfo(item.getentityid()); if (info.containsfeature("http://jabber.org/protocol/muc")) { answer.add(item.getentityid()); } } catch (xmppexception e) { } } } return answer; } private static void configure(providermanager pm) { // service discovery # items pm.addiqprovider("query", "http://jabber.org/protocol/disco#items", new discoveritemsprovider()); // service discovery # info pm.addiqprovider("query", "http://jabber.org/protocol/disco#info", new discoverinfoprovider()); // service discovery # items pm.addiqprovider("query", "http://jabber.org/protocol/disco#items", new discoveritemsprovider()); // service discovery # info pm.addiqprovider("query", "http://jabber.org/protocol/disco#info", new discoverinfoprovider()); //offline message requests pm.addiqprovider("offline","http://jabber.org/protocol/offline", new offlinemessagerequest.provider()); //offline message indicator pm.addextensionprovider("offline","http://jabber.org/protocol/offline", new offlinemessageinfo.provider()); //vcard pm.addiqprovider("vcard","vcard-temp", new vcardprovider()); //filetransfer pm.addiqprovider("si","http://jabber.org/protocol/si", new streaminitiationprovider()); pm.addiqprovider("query","http://jabber.org/protocol/bytestreams", new bytestreamsprovider()); pm.addiqprovider("open","http://jabber.org/protocol/ibb", new ibbproviders.open()); pm.addiqprovider("close","http://jabber.org/protocol/ibb", new ibbproviders.close()); pm.addextensionprovider("data","http://jabber.org/protocol/ibb", new ibbproviders.data()); //data forms pm.addextensionprovider("x", "jabber:x:data", new dataformprovider()); //html pm.addextensionprovider("html", "http://jabber.org/protocol/xhtml-im", new xhtmlextensionprovider()); //ad-hoc command pm.addiqprovider("command", "http://jabber.org/protocol/commands", new adhoccommanddataprovider()); // chat state chatstateextension.provider chatstate = new chatstateextension.provider(); pm.addextensionprovider("active", "http://jabber.org/protocol/chatstates", chatstate); pm.addextensionprovider("composing", "http://jabber.org/protocol/chatstates", chatstate); pm.addextensionprovider("paused", "http://jabber.org/protocol/chatstates", chatstate); pm.addextensionprovider("inactive", "http://jabber.org/protocol/chatstates", chatstate); pm.addextensionprovider("gone", "http://jabber.org/protocol/chatstates", chatstate); //muc user,admin,owner pm.addextensionprovider("x", "http://jabber.org/protocol/muc#user", new mucuserprovider()); pm.addiqprovider("query", "http://jabber.org/protocol/muc#admin", new mucadminprovider()); pm.addiqprovider("query", "http://jabber.org/protocol/muc#owner", new mucownerprovider()); } private static void initfeatures(xmppconnection xmppconnection) { servicediscoverymanager.setidentityname("android_im"); servicediscoverymanager.setidentitytype("phone"); servicediscoverymanager sdm = servicediscoverymanager.getinstancefor(xmppconnection); if(sdm == null) { sdm = new servicediscoverymanager(xmppconnection); } sdm.addfeature("http://jabber.org/protocol/disco#info"); sdm.addfeature("http://jabber.org/protocol/caps"); sdm.addfeature("urn:xmpp:avatar:metadata"); sdm.addfeature("urn:xmpp:avatar:metadata+notify"); sdm.addfeature("urn:xmpp:avatar:data"); sdm.addfeature("http://jabber.org/protocol/nick"); sdm.addfeature("http://jabber.org/protocol/nick+notify"); sdm.addfeature("http://jabber.org/protocol/xhtml-im"); sdm.addfeature("http://jabber.org/protocol/muc"); sdm.addfeature("http://jabber.org/protocol/commands"); sdm.addfeature("http://jabber.org/protocol/si/profile/file-transfer"); sdm.addfeature("http://jabber.org/protocol/si"); sdm.addfeature("http://jabber.org/protocol/bytestreams"); sdm.addfeature("http://jabber.org/protocol/ibb"); sdm.addfeature("http://jabber.org/protocol/feature-neg"); sdm.addfeature("jabber:iq:privacy"); } }
上面有两张spark客户端的聊天室列表占有者一列不同的原因:当使用以下代码获取时不能获取occupantscount和subject的值:
system.out.println(roominfo.getoccupantscount() + " : " + roominfo.getsubject());
这是由于openfire端有个bug(暂且这样说吧,我不知为什么openfire这样做).首先修改smack的一个bug,修改roominfo类的roominfo(discoverinfo info) 方法:
iterator<string> values = form.getfield("muc#roominfo_subject").getvalues(); if (values.hasnext()) { this.subject = values.next(); } else { this.subject = ""; }
改为:
final formfield subjfield = form.getfield("muc#roominfo_subject"); this.subject = subjfield == null ? "" : subjfield.getvalues().next();
修改openfire的源码,org/jivesoftware/openfire/muc/spi/multiuserchatserviceimpl类的public dataform getextendedinfo(string name, string node, jid senderjid) 方法:
/*final formfield fieldocc = dataform.addfield(); */ fieldsubj.setvariable("muc#roominfo_occupants"); fieldsubj.setlabel(localeutils.getlocalizedstring("muc.extended.info.occupants")); fieldsubj.addvalue(integer.tostring(room.getoccupantscount()));
改为:
final formfield fieldoccu= dataform.addfield();
fieldoccu.setvariable("muc#roominfo_occupants");
fieldoccu.setlabel(localeutils.getlocalizedstring("muc.extended.info.occupants"));
fieldoccu.addvalue(integer.tostring(room.getoccupantscount()));
使用asmack开发基于android的im系统同理.