最近在學習XMPP的使用,打算完成一個完整較爲完整地Demo示例,通過這個示例掌握xmpp的使用與開發。同時打算在這個示例中學習使用一下其他的開源類庫,在此作爲記錄學習。
包括服務器端--Openfire,客戶端--Spark,XMPP 語言的實現,因此對於熟悉的Java的開發者來說不是很難。
 
Openfire的介紹
Openfire的採用Java的開發,開源的實時協作(RTC)服務器基於XMPP(Jabber的)協議。
可以您使用它輕易的構建高效率的即時通信服務器。
Openfire安裝和使用都非常簡單,並利用Web進行管理。單臺服務器可支持上萬併發用戶。
由於是採用開放的XMPP協議,您可以使用各種支持XMPP協議的IM客戶端軟件登陸服務.
星火介紹
火花提供了客戶端一個基本的實現,並提出了一個很好的插件架構,這對於開發者來說不能不說是一個福音。我強烈建議基於插件方式來實現你新增加的功能,而不是去改它的源代碼,這樣有利於你項目架構,把原始項目的影響降到最低,文章以後的部分也是基於這種插件體系進行開發的
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、輸入用戶名、密碼、服務器地址,可以在搭建好openfire服務器後用spark註冊賬號,類似qq,多註冊幾個作爲測試用,我用spark建立賬號名和密碼都爲test,在此賬號創建2個組,我的好友和大學同學,同時註冊test1-test4的測試賬號,加test爲好友,當然這些都可以用代碼操作,初始階段我是這麼做快速寫代碼測試。
2,輸入錯誤的賬號和密碼會看到信息展示在登陸失敗後將錯誤信息展示出來
3、當輸入正確用戶名和密碼之後,進入mainActivity界面,這裏將分組和所有好友以文字形式展現出來
 
代碼:
XMPPManager.java
包管理器;進口org.jivesoftware.smack *;進口org.jivesoftware.smack.provider.ProviderManager;進口org.jivesoftware.smackx.GroupChatInvitation;進口org.jivesoftware.smackx.PrivateDataManager;進口org.jivesoftware.smackx.packet。 *;進口org.jivesoftware.smackx.provider *;進口org.jivesoftware.smackx.search.UserSearch;進口java.util.Collection中;進口java.util.Iterator的; / ** *用戶:Coolwxb *日期:14-1-13 *時間:下午5:10 * / public類XMPPManager {私有靜態XMPPManager例如= NULL; XMPPConnection連接= NULL;私人靜態字符串URL =“10.0.0.14”;私有靜態詮釋端口= 5222;私人靜態字符串DEVICE =“PC”;私有靜態字符串username =“測試”;私有靜態字符串密碼=“測試”;私有靜態詮釋成功= 0;民營XMPPManager(){/ **管理提供商解析XMPP包的自定義XML子文檔。兩種類型的供應商存在: IQProvider - 解析請求智商爲Java對象。 PacketExtension - 解析附包進入PacketExtension實例XML子文檔。 ** / ProviderManager的下午= ProviderManager.getInstance(); 配置(下午); } / ** *單例方法 * @返回* /公共靜態XMPPManager的getInstance(){如果(例如== NULL){ 例如=新XMPPManager(); }返回實例; } / ** *獲得連接連接 * @返回* /公共XMPPConnection的getConnection()拋出異常{如果(連接== NULL)拋出新的異常(“請先初始化xmppconnection”);返回連接; } / ** * *登陸操作返回字符串來判斷登陸結果 代碼XMPP錯誤類型 500 INTERNA服務器錯誤WAIT 403禁止AUTH 400bad請求MODIFY 404項未找到取消 409取消衝突 501功能沒有實現取消 302走了MODIFY 400 JID-畸形MODIFY 406不接受MODIFY 405未允許取消 401未授權AUTH 402所需支付-AUTH 404收件人,無法等待 302重定向MODIFY 407註冊要求的AUTH 404遠程服務器未找到取消 504遠程服務器超時等待 502遠程服務器錯誤取消 500資源約束WAIT 503服務不可用取消 407訂閱所需AUTH 500不確定條件WAIT 400意想不到的條件等待 408請求超時取消 * * @參數名 * @參數密碼 * @參數服務器* /公共字符串isLogin(字符串的用戶名,密碼字符串,字符串服務器){{嘗試 ConnectionConfiguration CF =新ConnectionConfiguration( 服務器, PORT, DEVICE); cf.setDebuggerEnabled(真); //開啓調試模式cf.setCompressionEnabled(假); //是否對流進行壓縮cf.setSASLAuthenticationEnabled(假); //是否開啓SASL登陸驗證連接=新XMPPConnection(CF); connection.connect(); connection.login(用戶名,密碼);返回SUCCESS +“”; }趕上(XMPPException五){返回e.getMessage(); } }公共無效配置(ProviderManager的PM){//私有數據Storagepm.addIQProvider(“查詢”,“胡言亂語:智商:私”,新PrivateDataManager.PrivateDataIQProvider()); // {Timetry pm.addIQProvider(“查詢”,“胡言亂語:智商:時間” 的Class.forName(“org.jivesoftware.smackx.packet.Time”)); }趕上(ClassNotFoundException的E){ } // XHTMLpm.addExtensionProvider(“HTML”,“http://jabber.org/protocol/xhtml-im",new XHTMLExtensionProvider()); //名冊Exchangepm.addExtensionProvider(”ד,”閒聊:X:名冊“新RosterExchangeProvider()); //消息Eventspm.addExtensionProvider(”ד,”閒聊:X:事件“,新MessageEventProvider()); //聊天Statepm.addExtensionProvider(”活性“,”HTTP://閒聊.ORG /協議/ chatstates“,新ChatStateExtension.Provider()); pm.addExtensionProvider(“構成”,“http://jabber.org/protocol/chatstates”,新ChatStateExtension.Provider()); pm.addExtensionProvider(“暫停”,“http://jabber.org/protocol/chatstates”,新ChatStateExtension.Provider()); pm.addExtensionProvider(“無效”,“http://jabber.org/protocol/chatstates”,新ChatStateExtension.Provider()); pm.addExtensionProvider(“水漲船高”,“http://jabber.org/protocol/chatstates”,新ChatStateExtension.Provider()); // FileTransferpm.addIQProvider(“SI”,“http://jabber.org/protocol / SI“,新StreamInitiationProvider()); //羣聊Invitationspm.addExtensionProvider(”X“,”胡言亂語:X:發佈會“,新GroupChatInvitation.Provider()); //服務發現#Itemspm.addIQProvider(”查詢“ “http://jabber.org/protocol/disco#items",new DiscoverItemsProvider()); //服務發現#Infopm.addIQProvider(”查詢“,”http://jabber.org/protocol/disco#info “新DiscoverInfoProvider()); //數據Formspm.addExtensionProvider(”ד,”閒聊:X:數據“,新DataFormProvider()); // MUC Userpm.addExtensionProvider(”ד,”HTTP://閒聊.ORG /協議/ MUC#用戶“,新MUCUserProvider()); // MUC Adminpm.addIQProvider(”查詢“,”http://jabber.org/protocol/muc#admin",new MUCAdminProvider()); / / MUC Ownerpm.addIQProvider(“查詢”,“http://jabber.org/protocol/muc#owner",new MUCOwnerProvider()); //延遲Deliverypm.addExtensionProvider(”X“,”胡言亂語:X:延時“新DelayInformationProvider()); // Versiontry { pm.addIQProvider(“查詢”,“胡言亂語:智商:版” 的Class.forName(“org.jivesoftware.smackx.packet.Version”)); }趕上(ClassNotFoundException的E){ } // VCardpm.addIQProvider(“電子名片”,“名片-TEMP”,新VCardProvider()); //離線消息Requestspm.addIQProvider(“離線”,“http://jabber.org/protocol/offline",new OfflineMessageRequest.Provider()); //離線消息Indicatorpm.addExtensionProvider(“離線”,“http://jabber.org/protocol/offline”,新OfflineMessageInfo.Provider()); //最後Activitypm.addIQProvider(“查詢“,”胡言亂語:智商:最後一個“,新LastActivity.Provider()); //用戶Searchpm.addIQProvider(”查詢“,”胡言亂語:智商:搜索“,新UserSearch.Provider()); // SharedGroupsInfo.Provider()); // JEP-33:擴展詩節Addressingpm.addExtensionProvider(“地址”,“http://jabber.org/protocol/address”,新MultipleAddressesProvider()); } / ** *返回組信息的容器 * @參數名冊 * @返回* /公衆的Iterator <RosterGroup> getGroups(名冊名單){ 收藏<RosterGroup> rosterGroups = roster.getGroups();返回rosterGroups.iterator(); } }
&#160;
這裏寫了個XMPPManager作爲幫助類,主要重要的方法就是對xmppconnection做初始化,可看到configure方法對用xml描述的xmpp包做解析,官方文檔是這樣對ProviderManager這個類做解釋:
&#160;
管理解析XMPP包的自定義XML子文檔供應商。
兩種類型的供應商存在: IQProvider - 解析請求智商爲Java對象。 PacketExtension - 解析附包XML子文檔
到PacketExtension實例。
&#160;
LoginActivity.java
包com.example.XMPPDemo;進口android.app.Activity;進口android.app.ProgressDialog;進口android.content.Intent;進口android.os.AsyncTask;進口android.os.Bundle;進口android.util.Log;進口android.view.View;進口android.widget.Button;進口android.widget.EditText;進口android.widget.TextView;進口manager.XMPPManager;進口org.jivesoftware.smack.XMPPConnection; / ** *用戶:Coolwxb *日期:14-1-13 *時間:下午5:18 * / public類LoginActivity擴展活動{私人XMPPManager xmppManager;私人XMPPConnection xmppConnection;私人按鈕btn_login;私人的EditText et_username;私人的EditText et_password;私人的EditText et_server;私人TextView的tv_info; ProgressDialog PD; @Overrideprotected無效的onCreate(捆綁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 =(按鈕)findViewById(R.id.btn_login); btn_login.setOnClickListener(新View.OnClickListener(){ @Overridepublic無效的onClick(視圖v){ 登錄(); } }); }私人無效登錄(){新的AsyncTask <String,字符串,字符串>(){ 字符串username =“”; 字符串密碼=“”; 字符串服務器=“”; @Overrideprotected無效onPreExecute(){ 用戶名= et_username.getText()的toString()修剪()。 。密碼= et_password.getText()的toString()修剪(); 服務器= et_server.getText()的toString()修剪()。 xmppManager = XMPPManager.getInstance(); PD =新ProgressDialog(LoginActivity.this); pd.setTitle(“提示”); pd.setMessage(“正在登陸......”); pd.show(); } @Overrideprotected字符串doInBackground(字符串...字符串){返回xmppManager.isLogin(用戶名,密碼,服務器); } @Overrideprotected無效onPostExecute(字符串信息){ pd.dismiss();如果(“0”.equals(信息)){//成功登陸SUCCESSIntent意圖=新意圖(); intent.setClass(LoginActivity.this,MainActivity.class); startActivity(意向); }其他{ Log.e(“錯誤”的信息); tv_info.setText(信息); } } } .execute(NULL,NULL,NULL); } }
這裏使用了一個機器人的異步線程類AsyncTask,在doInBackground方法中做一些耗時的操作,我試過如果不加入線程中操作的話會出現假死現象,所以在這裏我將登陸連接放入了線程中.
&#160;
MainActivity.java
包com.example.XMPPDemo;進口android.app.Activity;進口android.os.Bundle;進口android.util.Log;進口android.widget.TextView;進口manager.XMPPManager;進口org.jivesoftware.smack.Roster;進口org.jivesoftware.smack.RosterEntry;進口org.jivesoftware.smack.RosterGroup;進口java.util.Collection中;進口java.util.Iterator的; / ** *用戶:Coolwxb *日期:14-1-13 *時間:下午6:28 * / public類MainActivity延伸活動{ XMPPManager xmppManager;私人TextView的tv_groupInfo; @Overrideprotected無效的onCreate(捆綁savedInstanceState){super.onCreate(savedInstanceState); 的setContentView(R.layout.activity_main); tv_groupInfo =(的TextView)findViewById(R.id.tv_groupinfo); xmppManager = XMPPManager.getInstance(); 名冊名冊= NULL;嘗試{ 名冊= xmppManager.getConnection()getRoster()。 }趕上(例外五){ e.printStackTrace(); } 迭代器<RosterGroup> rosterGroupIterator = xmppManager.getGroups(名冊); StringBuilder的SB =新的StringBuilder(); //獲取所有組信息,如果(rosterGroupIterator.hasNext()){//如果有分組,而(rosterGroupIterator.hasNext()){ RosterGroup rosterGroup = rosterGroupIterator.next(); 串組名= rosterGroup.getName();詮釋計數= rosterGroup.getEntryCount(); sb.append(rosterGroup.getName()+“\ r \ n”); } tv_groupInfo.setText(“組信息:+ \ r \ n”+ sb.toString()); }其他{ tv_groupInfo.setText(“沒有組信息”); } //獲取所有人集合<RosterEntry> =迭代器roster.getEntries(); 迭代器<RosterEntry> entryIterator = iterator.iterator(); SB =新的StringBuilder();而(entryIterator.hasNext()){ RosterEntry rosterEntry = entryIterator.next(); sb.append(rosterEntry.getName()+“\ r \ n”); } 串二= sb.toString(); Log.i(“信息”,二); tv_groupInfo.append(“所有的成員:+ \ r \ n”+ II); } }
MainActivity的界面中我只放入了一個textview,用來顯示分組信息和成員。因爲在操作開始我用spark向test這個賬號建立了分組和好友,所以會獲取到。
登陸就介紹到這裏,跳轉到mainactivity獲取組信息和成員信息涉及比較多的常用類,打算專門研究下專門寫一章。
&#160;
最後附上一些比較重要的東東,希望大家會喜歡,同時也希望大家提出寶貴意見,大家共同進步。
&#160;
登陸源碼: