目录

我的学习分享

记录精彩的程序人生

X

即时通讯-openfire、smack、spark

基于openfire 服务和smack 搭建即时通讯项目

下面是我在本地搭建的open fire,非常简单。
20181107112057229.png

点击下载open fire服务,和spark客户端,安装过程也非常的傻瓜,就不在这里说明了,如果遇到问题欢迎留言。
局域网内实现通讯,需要更改 host 文件 :C:\Windows\System32\drivers\etc\host,
#localOpenfire(IP + 空格 + 域名)192.168.4.123 openfire.im.hexl。具体如何在spark中配置自行百度。
spark 配置参数

20181107113837456.PNG

20181107113820911.PNG

下面是Android端的demo,具体实现包括源码地址会在之后的博客中给出。

2018110711585839.gif

连接、注册、登录

开启连接

  • 首先需要导入smack相关jar包,篇尾会给出,至于源码会在该系列的博客最后一篇给出来,如果有问题欢迎大家留言

  • 打开连接相关API
    1、XMPPTCPConnection
    2、XMPPTCPConnectionConfiguration

  • 在开启连接时用到的就是以上两个api,下面看下如何去创建,通过查看源码我们发现创建XMPPTCPConnection时需要传入一个config对象也就是XMPPTCPConnectionConfiguration,关键也在这块,该对象里面包含了一些非常重要的方法如下:

	//设置openfire主机IP
	setHostAddress(InetAddress.getByName(SERVER_HOST))
	//设置openfire服务器名称
	setXmppDomain(SERVER_NAME)
	//设置默认端口号:默认5222
	setPort(SERVER_PORT)
	//禁用SSL连接
	setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
	setCompressionEnabled(false)
	//设置debug模式
	setDebuggerEnabled(true)
	//设置离线状态
	setSendPresence(false)
	//设置开启压缩,可以节省90%流量
	setCompressionEnabled(true);
  • 连接xmpp服务的具体code
    public boolean openConnection() {
        try {
            if (null == connection || !connection.isAuthenticated()) {
                SmackConfiguration.DEBUG = true;
                XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration.builder()
                        //设置openfire主机IP
                        .setHostAddress(InetAddress.getByName(SERVER_HOST))
                        //设置openfire服务器名称
                        .setXmppDomain(SERVER_NAME)
                        //设置默认端口号:默认5222
                        .setPort(SERVER_PORT)
                        //禁用SSL连接
                        .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                        .setCompressionEnabled(false)
                        //设置debug模式
                        .setDebuggerEnabled(true)
                        //设置离线状态
                        .setSendPresence(false)
                        //设置开启压缩,可以节省90%流量
                        .setCompressionEnabled(true);

                //需要经过同意才可以添加好友
                Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.manual);

                //连接服务器
                connection = new XMPPTCPConnection(config.build());
                connection.connect();
                Log.e(TAG, "连接服务器成功");
                return true;
            }
        } catch (XMPPException | SmackException | IOException | InterruptedException xe) {
            xe.printStackTrace();
            connection = null;
            Log.e(TAG, "连接服务器失败");
        }
        return false;
    }

以上基本完成连接配置并成功连接到你本地的服务。

注册

  • 注册相关api
    1、AccountManager
  • 获取AccountManager对象实例
	AccountManager.getInstance(Connection conn);
  • 创建账户
	createAccount(account, password);
  • 注册code
    /**
     * 注册
     *
     * @param account  账号
     * @param password 密码
     * @param attr     用户资料 attr对应的一些属性值 name;email;first;last;city;state;zip;
     *                 phone;url;date;misc;text;remove(可以查看AccountManager中的getAccountAttributes()函数注释)
     */
    public boolean register(String account, String password, Map<String, String> attr) {
        try {
            //获取AccountManger实例
            AccountManager manager = getAccountManager();
            //服务器支持创建新账户
            if (manager.supportsAccountCreation()) {
                if (null == attr) {
                    //创建账户
                    manager.createAccount(Localpart.from(account), password);
                } else {
                    //创建账户并且涵盖map集合中的一些其他信息
                    manager.createAccount(Localpart.from(account), password, attr);
                }
                Log.e(TAG, "注册成功");
                return true;
            }
        } catch (XMPPException | IOException | SmackException | InterruptedException xe) {
            xe.printStackTrace();
            Log.e(TAG, "注册失败");
        }
        return false;
    }

以上是注册code

登录

  • 登录相关api
    1、XMPPTCPConnection

  • 登录

	login(account, password);
  • 登录code
    /**
     * 登录
     *
     * @param account  账号
     * @param password 密码
     */
    public boolean login(String account, String password) {
        try {
            getConnection().login(account, password);
            Log.e(TAG, "登录成功");
            return true;
        } catch (XMPPException | IOException | SmackException | InterruptedException xe) {
            xe.printStackTrace();
            Log.e(TAG, "登录失败");
        }
        return false;
    }

以上是登录code

----------------------------------------------------------我是分割线--------------------------------------------------------------

接下来给大家看下效果图
20181107132845495.gif

smack4.2.1-jar包下载地址

添加好友、删除好友、好友列表、添加好友及被添加监听

20181108153245646.gif

这里向大家介绍关于好友管理的API

  • Roster(花名册):主要用到的就是以下三个方法,当然更多的API我还没有用到,后续如果用到的话会补充到该博客中。
  /**
   * 添加好友
   *
   * @param user   即为 jid 唯一标识
   * @param name   名称:昵称 or 备注
   * @param groups 分组:想要添加到哪个分组
   * /
   createEntry(BareJid user, String name, String[] groups)
   
   /**
 	* 删除好友
 	*/
   removeEntry(RosterEntry entry);
   
   /**
    * 获取好友列表
    */
   getEntries();

  • StanzaListener(房间监听器):每次状态改变时都会回调processStanza(Stanza packet)这个方法。
  • Stanza:可以通过如下Type类型来进行区分好友状态available,unavailable,subscribe, subscribed, unsubscribe, unsubscribed,error, probe

添加好友

添加好友代码

    /**
     * 添加好友
     *
     * @param user   即为 jid 唯一标识
     * @param name   名称:昵称 or 备注
     * @param groups 分组:想要添加到哪个分组
     * @return
     */
    public boolean addFriend(String user, String name, String[] groups) {
        try {
            getRoster().createEntry(Objects.requireNonNull(Utils.stringToJid(user)), name, groups);
            Log.e(TAG, "添加成功");
            return true;
        } catch (XMPPException | SmackException | InterruptedException xe) {
            xe.printStackTrace();
            Log.e(TAG, "添加失败");
        }
        return false;
    }

删除好友

删除好友代码

/**
 * 删除好友
 */
 public boolean deleteFriend(BareJid jid) {
     try {
         Roster roster = getRoster();
         RosterEntry entry = roster.getEntry(jid);
         if (entry != null) {
             roster.removeEntry(entry);
             Log.e(TAG, "删除成功");
             return true;
         }
     } catch (XMPPException | SmackException | InterruptedException xe) {
         xe.printStackTrace();
         Log.e(TAG, "删除失败");
     }
     return false;
 }

好友列表

获取好友列表

	/**
     * 获取好友列表
     *
     * @return 好友列表
     */
    public List<RosterEntry> getFriends() {
        Collection<RosterEntry> entries = Roster.getInstanceFor(getConnection()).getEntries();
        return new ArrayList<>(entries);
    }

添加好友及被添加监听

好友实体类

public class AddFriendEntity implements Parcelable {

    //对方名称
    private java.lang.String toName;
    //对方jid
    private Jid toJid;
    //主动添加好友的名称
    private java.lang.String fromName;
    //主动添加好友的jid
    private Jid fromJid;
    //状态 1: 同意,2: 取消,3: 拒绝,4: 收到请求
    private int state;
}

监听器
这里说下,如果想要通知Fragment和Activity进行刷新UI,可以采用观察者模式(Observer),在最后会给出观察者(observer)的相关代码。

public class XMAddFriendListener implements StanzaListener {
    public static final String TAG = XMAddFriendListener.class.getSimpleName();
    
    private int count;

    private AddFriendEntity addFriendEntity = new AddFriendEntity();

    @Override
    public void processStanza(Stanza stanza) {
        Log.e(TAG, "stanza.toXML():" + stanza.toXML());
        if (stanza instanceof Presence) {
            Presence presence = (Presence) stanza;
            switch (presence.getType()) {
                case subscribe://收到添加请求
                    Log.e(TAG, "收到添加请求");
                    //A用户多次请求添加B用户视为一次请求。
                    if (!presence.getFrom().equals(addFriendEntity.getFromJid())) {
                        count++;
                        addFriendEntity.setFromJid();//发送方Jid
                        addFriendEntity.setFromName();//发送方名称
                        addFriendEntity.setToJid();//接收方jid
                        addFriendEntity.setToName();//接受方名称
                        addFriendEntity.setState(4);//TODO 后期优化为枚举,增加阅读性
                        observable.setEntity(addFriendEntity, count);
                    }
                    break;
                case subscribed://同意订阅(对方同意添加好友)
                    Log.e(TAG, "对方同意添加好友");
                    addFriendEntity.setState(1);
                    observable.setEntity(addFriendEntity, count);
                    break;
                case unsubscribe://取消订阅
                    addFriendEntity.setState(3);
                    observable.setEntity(addFriendEntity, count);
                    break;
                case unsubscribed://拒绝订阅
//                    addFriendEntity.setState(3);
//                    observable.setEntity(addFriendEntity, count);
                    System.out.println("拒绝订阅");
                    break;
                case unavailable:
                    System.out.println("好友下线!");
                    break;
                case available:
                    System.out.println("好友上线!");
                    break;
                case error:
                    System.out.println("发生错误");
                    break;
                case probe:
                    break;
                default:
                    break;
            }
        }
    }
}

被观察者

/**
 * @author Hexl
 * @desc 被观察者
 * @date 2018/10/19
 */
public class FriendObservable extends Observable {
    private AddFriendEntity entity;
    private int friendNum;

    public AddFriendEntity getEntity() {
        return entity;
    }

    public int getFriendNum() {
        return friendNum;
    }

    public void setEntity(AddFriendEntity entity, int friendNum) {
        this.entity = entity;
        this.friendNum = friendNum;
        setChanged();
        notifyObservers(entity);
    }
}

观察者

/**
 * @author Hexl
 * @desc 观察者
 * @date 2018/10/19
 */
public interface FriendObserver extends Observer {
    @Override
    void update(Observable addFriend, Object arg);
}

增加观察者后的监听器代码
最后建议大家可以在Activity或Fragment中的onCreate方法调用addObserver(Observer observer)然后在onDestroy方法调用removeObserver(Observer observer)不然会使内存导致内存泄漏。

/**
 * @author Hexl
 * @desc 添加好友监听
 * @date 2018/10/27
 */
public class XMAddFriendListener implements StanzaListener {
    public static final String TAG = XMAddFriendListener.class.getSimpleName();
    
    public static FriendObservable observable = new FriendObservable();

    public static void addObserver(Observer observer) {
        observable.addObserver(observer);
    }

    public static void removeObserver(Observer observer) {
        observable.deleteObserver(observer);
    }

    private int count;

    private AddFriendEntity addFriendEntity = new AddFriendEntity();

    @Override
    public void processStanza(Stanza stanza) {
        Log.e(TAG, "stanza.toXML():" + stanza.toXML());
        if (stanza instanceof Presence) {
            Presence presence = (Presence) stanza;
            switch (presence.getType()) {
                case subscribe://收到添加请求
                    Log.e(TAG, "收到添加请求");
                    //A用户多次请求添加B用户视为一次请求。
                    if (!presence.getFrom().equals(addFriendEntity.getFromJid())) {
                        count++;
                        addFriendEntity.setFromJid();//发送方Jid
                        addFriendEntity.setFromName();//发送方名称
                        addFriendEntity.setToJid(presence.getTo());//接收方jid
                        addFriendEntity.setToName();//接受方名称
                        addFriendEntity.setState();//TODO 后期优化为枚举,增加阅读性
                        observable.setEntity(addFriendEntity, count);
                    }
                    break;
                case subscribed://同意订阅(对方同意添加好友)
                    Log.e(TAG, "对方同意添加好友");
                    addFriendEntity.setState(1);
                    observable.setEntity(addFriendEntity, count);
                    break;
                case unsubscribe://取消订阅
                    addFriendEntity.setState(3);
                    observable.setEntity(addFriendEntity, count);
                    break;
                case unsubscribed://拒绝订阅
//                    addFriendEntity.setState(3);
//                    observable.setEntity(addFriendEntity, count);
                    System.out.println("拒绝订阅");
                    break;
                case unavailable:
                    System.out.println("好友下线!");
                    break;
                case available:
                    System.out.println("好友上线!");
                    break;
                case error:
                    System.out.println("发生错误");
                    break;
                case probe:
                    break;
                default:
                    break;
            }
        }
    }
}

创建聊天室、发送聊天室消息、邀请好友加入聊天室、获取服务器上所有聊天室、获取当前用户所在聊天室、退出聊天室、查询聊天室成员名称

效果图奉上:
20181109163201559.gif

创建聊天室

先跟大家介绍一下创建聊天室相关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();
        }
    }
————————————————
版权声明:本文为CSDN博主「埋心。」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_31910313/article/details/83896273    /**
     * 退出聊天室
     *
     * @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;
    }


版权声明:本文为CSDN博主「埋心。」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_31910313/article/details/83896273