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()+"\r\n");
        }
        tv_groupInfo.setText("組信息:+\r\n"+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()+"\r\n");
        }
        String ii = sb.toString();
        Log.i("info", ii);
        tv_groupInfo.append("所有的成員:+\r\n"+ii);

    }
}

MainActivity的界面中我只放入了一個textview,用來顯示分組信息和成員。因爲在操作開始我用spark向test這個賬號建立了分組和好友,所以會獲取到。

登陸就介紹到這裏,跳轉到mainactivity獲取組信息和成員信息涉及比較多的常用類,打算專門研究下專門寫一章。

 

最後附上一些比較重要的東東,希望大家會喜歡,同時也希望大家提出寶貴意見,大家共同進步。

 

登陸源碼:

要XMPP登陸DEMO源碼的點我!。。。。

xmpp幫助文檔:

要英文官方chm格式指導手冊的點我!。。。

要chm格式的XMPP協議API文檔的點我!。。。。

asmack jar包

要開發asmack 開發jar包的點我!。。。。

 

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章