Android中基于XMPP协议实现IM聊天程序与多人聊天室

作者:喝醉的毛毛虫 时间:2022-05-06 07:37:51 

简单的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

Android中基于XMPP协议实现IM聊天程序与多人聊天室

Android中基于XMPP协议实现IM聊天程序与多人聊天室


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的信息。

Android中基于XMPP协议实现IM聊天程序与多人聊天室

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

Android中基于XMPP协议实现IM聊天程序与多人聊天室

Android中基于XMPP协议实现IM聊天程序与多人聊天室

Android中基于XMPP协议实现IM聊天程序与多人聊天室

Android中基于XMPP协议实现IM聊天程序与多人聊天室

Android中基于XMPP协议实现IM聊天程序与多人聊天室

Android中基于XMPP协议实现IM聊天程序与多人聊天室

使用asmack开发基于Android的IM系统同理.

标签:Android,聊天
0
投稿

猜你喜欢

  • android实现通过NFC读取卡号

    2023-06-24 08:45:54
  • SpringBoot 整合RabbitMq 自定义消息监听容器来实现消息批量处理

    2021-10-11 22:36:07
  • C#使用foreach语句简单遍历数组的方法

    2022-02-07 08:40:24
  • 教你怎么用Java数组和链表实现栈

    2023-10-29 08:13:57
  • Android编程实现的短信编辑器功能示例

    2022-09-08 12:58:44
  • Spring Boot整合Mybatis并完成CRUD操作的实现示例

    2023-11-09 04:36:46
  • C#字典Dictionary的用法说明(注重性能版)

    2023-08-05 19:33:02
  • C#获取图片的后缀名解析

    2023-02-13 09:35:58
  • java遍历properties文件操作指南

    2023-11-23 02:43:45
  • Android如何给按钮添加点击音效

    2022-06-24 02:05:05
  • C#算法设计与分析详解

    2023-03-03 10:15:06
  • Spring Boot缓存实战 EhCache示例

    2023-08-30 12:23:35
  • Struts2实现文件上传时显示进度条功能

    2021-10-13 05:22:22
  • 一文搞懂C++中的运算符重载

    2021-08-02 02:47:53
  • Android使用fragment实现左侧导航

    2023-10-30 10:02:09
  • 快速了解如何在.NETCORE中使用Generic-Host建立主机

    2022-05-08 15:04:12
  • SpringBoot集成支付宝沙箱支付(支付、退款)

    2022-02-15 16:50:52
  • Springboot-注解-操作日志的实现方式

    2023-10-10 13:54:35
  • Java NegativeArraySizeException异常解决方案

    2023-10-31 07:16:51
  • 深入浅出重构Mybatis与Spring集成的SqlSessionFactoryBean(上)

    2021-12-01 18:27:49
  • asp之家 软件编程 m.aspxhome.com