• XMPP学习——2、用户登录


    最近在学习XMPP的使用,打算完成一个完整较为完整地Demo示例,通过这个示例掌握xmpp的使用与开发。同时打算在这个示例中学习使用一下其他的开源类库,在此作为记录学习。

    包括服务器端——Openfire,客户端——Spark,XMPP 传输协议的实现——Smack(XMPP是一个协议,协议是需要实现的,Smack起到的就是这样的一个作用,android开发使用的是asmack类库)。三者都是基于Java 语言的实现,因此对于熟悉Java 的开发者来说不是很难。

    OpenFire介绍

    Openfire 采用Java开发,开源的实时协作(RTC)服务器基于XMPP(Jabber)协议。

    您可以使用它轻易的构建高效率的即时通信服务器.

    Openfire安装和使用都非常简单,并利用Web进行管理。单台服务器可支持上万并发用户。

    由于是采用开放的XMPP协议,您可以使用各种支持XMPP协议的IM客户端软件登陆服务.

    Spark介绍

    Spark 提供了客户端一个基本的实现,并提出了一个很好的插件架构,这对于开发者来说不能不说是一个福音。我强烈建议基于插件方式来实现你新增加的功能,而不是去改它的源代码,这样有利于你项目架构,把原始项目的影响降到最低,文章以后的部分也是基于这种插件体系进行开发的

    Asmack介绍

    smack的Android版本,虽然Smack在PC上可以工作的很好,功能也很强大,但在Android平台上有一些问题,而导致这些问题的原因是Android精简了Java的类库,以至Smack使用的部分类库在Android平台上无法找到,所以Smack不能直接在Android平台上使用.但在2010年初,有人在code.google.com网站上发布了一个Asmack,其中A库就代表Android中的A,也就是说,这个版本是Smack的Android版本.可使用下面地址下载Asmack:

    http://code.google.com/p/asmack/downloads/list

    下载asmack-2010.03.03.jar文件后,直接在Android工程中引用即可

    关系图

    1

    登陆操作界面:

    若水GIF截图_2014年1月13日21点5分26秒

    示意说明:

    1、输入用户名、密码、服务器地址,可以在搭建好openfire服务器后用spark注册账号,类似qq,多注册几个作为测试用,我用spark建立账号名和密码都为test,在此账号创建2个组,我的好友和大学同学,同时注册test1-test4的测试账号,加test为好友,当然这些都可以用代码操作,初始阶段我是这么做快速写代码测试。

    2、输入错误的账号和密码会看到信息展示在登陆失败后将错误信息展示出来

    3、当输入正确用户名和密码之后,进入mainActivity界面,这里将分组和所有好友以文字形式展现出来

     

    代码:

    XMPPManager.java

    package manager;
    
    import org.jivesoftware.smack.*;
    import org.jivesoftware.smack.provider.ProviderManager;
    import org.jivesoftware.smackx.GroupChatInvitation;
    import org.jivesoftware.smackx.PrivateDataManager;
    import org.jivesoftware.smackx.packet.*;
    import org.jivesoftware.smackx.provider.*;
    import org.jivesoftware.smackx.search.UserSearch;
    
    import java.util.Collection;
    import java.util.Iterator;
    
    /**
     * User: Coolwxb
     * Date: 14-1-13
     * Time: 下午5:10
     */
    public class XMPPManager {
        private static XMPPManager instance = null;
        XMPPConnection connection=null;
        private static String URL = "10.0.0.14";
        private static int PORT = 5222;
        private static String DEVICE = "pc";
        private static String USERNAME = "test";
        private static String PASSWORD = "test";
        private static int SUCCESS=0;
        private XMPPManager( ){
            /**
            Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of providers exist:
            IQProvider -- parses IQ requests into Java objects.
            PacketExtension -- parses XML sub-documents attached to packets into PacketExtension instances.
             **/
            ProviderManager pm = ProviderManager.getInstance();
            configure(pm);
        }
    
        /**
         * 单例方法
         * @return
         */
        public static XMPPManager getInstance() {
            if (instance == null) {
                instance = new XMPPManager();
            }
            return instance;
        }
    
        /**
         * 获得connection连接
         * @return
         */
        public XMPPConnection getConnection() throws Exception {
    
            if (connection == null)
                throw new Exception("请先初始化xmppconnection");
                return connection;
    
        }
        /**
         *
         * 登陆操作  返回String 来判断登陆结果
         Code XMPP Error Type
         500 interna-server-error WAIT
         403 forbidden AUTH
         400bad-request MODIFY
         404 item-not-found CANCEL
         409 conflict CANCEL
         501 feature-not-implemented CANCEL
         302 gone MODIFY
         400 jid-malformed MODIFY
         406 no-acceptable MODIFY
         405 not-allowed CANCEL
         401 not-authorized AUTH
         402 payment-required AUTH
         404 recipient-unavailable WAIT
         302 redirect MODIFY
         407 registration-required AUTH
         404 remote-server-not-found CANCEL
         504 remote-server-timeout WAIT
         502 remote-server-error CANCEL
         500 resource-constraint WAIT
         503 service-unavailable CANCEL
         407 subscription-required AUTH
         500 undefined-condition WAIT
         400 unexpected-condition WAIT
         408 request-timeout CANCEL
         *
         * @param username
         * @param password
         * @param server*/
        public String isLogin(String username, String password, String server) {
            try {
                ConnectionConfiguration cf = new ConnectionConfiguration(
                        server,
                        PORT,
                        DEVICE);
                cf.setDebuggerEnabled(true);  //开启debug模式
                cf.setCompressionEnabled(false);  //是否对流进行压缩
                cf.setSASLAuthenticationEnabled(false); //是否开启SASL 登陆验证
                connection = new XMPPConnection(cf);
                connection.connect();
                connection.login(username, password);
                return SUCCESS+"";
            } catch (XMPPException e) {
                return e.getMessage();
            }
        }
    
        public void configure(ProviderManager pm) {
    
            // Private Data Storage
            pm.addIQProvider("query", "jabber:iq:private",
                    new PrivateDataManager.PrivateDataIQProvider());
    
            // Time
            try {
                pm.addIQProvider("query", "jabber:iq:time",
                        Class.forName("org.jivesoftware.smackx.packet.Time"));
            } catch (ClassNotFoundException e) {
            }
    
            // XHTML
            pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im",
                    new XHTMLExtensionProvider());
    
            // Roster Exchange
            pm.addExtensionProvider("x", "jabber:x:roster",
                    new RosterExchangeProvider());
            // Message Events
            pm.addExtensionProvider("x", "jabber:x:event",
                    new MessageEventProvider());
            // Chat State
            pm.addExtensionProvider("active",
                    "http://jabber.org/protocol/chatstates",
                    new ChatStateExtension.Provider());
            pm.addExtensionProvider("composing",
                    "http://jabber.org/protocol/chatstates",
                    new ChatStateExtension.Provider());
            pm.addExtensionProvider("paused",
                    "http://jabber.org/protocol/chatstates",
                    new ChatStateExtension.Provider());
            pm.addExtensionProvider("inactive",
                    "http://jabber.org/protocol/chatstates",
                    new ChatStateExtension.Provider());
            pm.addExtensionProvider("gone",
                    "http://jabber.org/protocol/chatstates",
                    new ChatStateExtension.Provider());
    
            // FileTransfer
            pm.addIQProvider("si", "http://jabber.org/protocol/si",
                    new StreamInitiationProvider());
    
            // Group Chat Invitations
            pm.addExtensionProvider("x", "jabber:x:conference",
                    new GroupChatInvitation.Provider());
            // 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());
            // Data Forms
            pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
            // MUC User
            pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user",
                    new MUCUserProvider());
            // MUC Admin
            pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin",
                    new MUCAdminProvider());
            // MUC Owner
            pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner",
                    new MUCOwnerProvider());
            // Delayed Delivery
            pm.addExtensionProvider("x", "jabber:x:delay",
                    new DelayInformationProvider());
            // Version
            try {
                pm.addIQProvider("query", "jabber:iq:version",
                        Class.forName("org.jivesoftware.smackx.packet.Version"));
            } catch (ClassNotFoundException e) {
            }
            // VCard
            pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
            // 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());
            // Last Activity
            pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
            // User Search
            pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
            // SharedGroupsInfo
            pm.addIQProvider("sharedgroup",
                    "http://www.jivesoftware.org/protocol/sharedgroup",
                    new SharedGroupsInfo.Provider());
            // JEP-33: Extended Stanza Addressing
            pm.addExtensionProvider("addresses",
                    "http://jabber.org/protocol/address",
                    new MultipleAddressesProvider());
    
        }
    
        /**
         * 返回组信息的容器
         * @param roster
         * @return
         */
        public Iterator<RosterGroup> getGroups(Roster roster) {
            Collection<RosterGroup> rosterGroups = roster.getGroups();
            return rosterGroups.iterator();
        }
    
    }

    这里写了个XMPPManager作为帮助类,主要重要的方法就是对xmppconnection做初始化,可看到configure方法对用xml描述的xmpp包做解析,官方文档是这样对ProviderManager这个类做解释:

    Manages providers for parsing custom XML sub-documents of XMPP packets. 
    Two types of providers exist:
    
    IQProvider -- parses IQ requests into Java objects. 
    PacketExtension -- parses XML sub-documents attached to packets 
    into PacketExtension instances.

    LoginActivity.java

    package com.example.XMPPDemo;
    
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.content.Intent;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.TextView;
    import manager.XMPPManager;
    import org.jivesoftware.smack.XMPPConnection;
    
    /**
     * User: Coolwxb
     * Date: 14-1-13
     * Time: 下午5:18
     */
    public class LoginActivity extends Activity{
        private XMPPManager xmppManager;
        private XMPPConnection xmppConnection;
    
        private Button btn_login;
        private EditText et_username;
        private EditText et_password;
        private EditText et_server;
        private TextView tv_info;
        ProgressDialog pd;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.login);
            et_password = (EditText) findViewById(R.id.et_password);
            et_username = (EditText) findViewById(R.id.et_username);
            et_server = (EditText) findViewById(R.id.et_server);
            tv_info = (TextView) findViewById(R.id.tv_info);
            btn_login = (Button) findViewById(R.id.btn_login);
            btn_login.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    login();
                }
            });
        }
    
        private void login() {
            new AsyncTask< String, String, String > (){
                String username = "";
                String password = "";
                String server = "";
                @Override
                protected void onPreExecute() {
                    username = et_username.getText().toString().trim();
                    password = et_password.getText().toString().trim();
                    server = et_server.getText().toString().trim();
                    xmppManager = XMPPManager.getInstance();
                    pd = new ProgressDialog(LoginActivity.this);
                    pd.setTitle("提示");
                    pd.setMessage("正在登陆。。。。");
                    pd.show();
                }
                @Override
                protected String doInBackground(String... strings) {
                   return xmppManager.isLogin(username,password,server);
            }
                @Override
                protected void onPostExecute(String info) {
                    pd.dismiss();
                    if ("0".equals(info)){
                            //成功登陆SUCCESS
                            Intent intent = new Intent();
                            intent.setClass(LoginActivity.this, MainActivity.class);
                            startActivity(intent);
                    }else {
                        Log.e("error",info);
                        tv_info.setText(info);
                    }
                }
            }.execute(null,null,null);
        }
    }

    这里使用了一个android 的异步线程类AsyncTask,在doInBackground方法中做一些耗时的操作,我试过如果不加入线程中操作的话会出现假死现象,所以在这里我将登陆连接放入了线程中.

     

    MainActivity.java

    package com.example.XMPPDemo;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.TextView;
    import manager.XMPPManager;
    import org.jivesoftware.smack.Roster;
    import org.jivesoftware.smack.RosterEntry;
    import org.jivesoftware.smack.RosterGroup;
    
    import java.util.Collection;
    import java.util.Iterator;
    
    /**
     * User: Coolwxb
     * Date: 14-1-13
     * Time: 下午6:28
     */
    public class MainActivity extends Activity{
        XMPPManager xmppManager;
        private TextView tv_groupInfo;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            tv_groupInfo = (TextView) findViewById(R.id.tv_groupinfo);
            xmppManager = XMPPManager.getInstance();
            Roster roster = null;
            try {
                roster = xmppManager.getConnection().getRoster();
            } catch (Exception e) {
                e.printStackTrace();
            }
            Iterator<RosterGroup> rosterGroupIterator = xmppManager.getGroups(roster);
            StringBuilder sb = new StringBuilder();
            //获取所有组信息
            if (rosterGroupIterator.hasNext()){  //如果有分组
            while (rosterGroupIterator.hasNext()) {
                RosterGroup rosterGroup = rosterGroupIterator.next();
                String groupName = rosterGroup.getName();
                int count = rosterGroup.getEntryCount();
                sb.append(rosterGroup.getName()+"
    ");
            }
            tv_groupInfo.setText("组信息:+
    "+sb.toString());
             }
            else{
                tv_groupInfo.setText("没有组信息");
            }
    
            //获取所有人
            Collection<RosterEntry> iterator  =roster.getEntries();
            Iterator<RosterEntry> entryIterator = iterator.iterator();
            sb = new StringBuilder();
            while (entryIterator.hasNext()) {
                RosterEntry rosterEntry = entryIterator.next();
                sb.append(rosterEntry.getName()+"
    ");
            }
            String ii = sb.toString();
            Log.i("info", ii);
            tv_groupInfo.append("所有的成员:+
    "+ii);
    
        }
    }

    MainActivity的界面中我只放入了一个textview,用来显示分组信息和成员。因为在操作开始我用spark向test这个账号建立了分组和好友,所以会获取到。

    登陆就介绍到这里,跳转到mainactivity获取组信息和成员信息涉及比较多的常用类,打算专门研究下专门写一章。

     

    最后附上一些比较重要的东东,希望大家会喜欢,同时也希望大家提出宝贵意见,大家共同进步。

     

    登陆源码:

    要XMPP登陆DEMO源码的点我!。。。。

    xmpp帮助文档:

    要英文官方chm格式指导手册的点我!。。。

    要chm格式的XMPP协议API文档的点我!。。。。

    asmack jar包

    要开发asmack 开发jar包的点我!。。。。

    最后吐槽下windows live writer 今天用的好卡。。。卡死3次。。什么心情。。

  • 相关阅读:
    数据结构和算法之最大堆
    AStar
    合并SkinnedMeshRender实现换装
    Asp.net MVC 解决:CS0308: 非泛型 方法“System.Web.Mvc.Html.LinkExtensions.ActionLink
    使用jquery解析Json , 浏览器之间数据格式校验的细微差异.
    Js跳转代码代替RedirectToAction是否违背MVC
    情 人 节 快 乐
    UCenter 1.5.0 整合 Bug
    ping [主机名] 4
    关于VS2008调试时端口号不一致的问题
  • 原文地址:https://www.cnblogs.com/coolwxb/p/3518129.html
Copyright © 2020-2023  润新知