XMPP協議簡介
XMPP協議(Extensible Messaging and PresenceProtocol,可擴展消息處理現場協議)是一種基於XML的協議,目的是爲了解決及時通信標準而提出來的,最早是在Jabber上實現的。它繼承了在XML環境中靈活的發展性。因此,基於XMPP的應用具有超強的可擴展性。並且XML很易穿過防火牆,所以用XMPP構建的應用不易受到防火牆的阻礙。利用XMPP作爲通用的傳輸機制,不同組織內的不同應用都可以進行有效的通信。
這篇文章有基本的介紹,http://blog.csdn.net/xutaozero21/article/details/4873439
IM
Instant Messenger,及時通信軟件,就是大家使用的QQ、MSN Messenger和Gtalk等等。其中Gtalk 就是基於XMPP 協議的一個實現,其他的則不是。當前IM 幾乎作爲每個上網者必然使用的工具,在國外的大型企業中有一些企業級的IM應用,但是其商業價值還沒完全發揮出來。設想既然XMPP 協議是一個公開的協議,那麼每個企業都可以利用它來開發適合本身企業工作,提高自身生產效率的IM;甚至,你還可以在網絡遊戲中集成這種通信軟件,不但讓你可以邊遊戲邊聊天,也可以開發出適合遊戲本身的IM 應用,比如說一些遊戲關鍵場景提醒功能,團隊語音交流等等都可以基於IM來實現。
本文主要講解在android使用xmpp協議進行即時通信,所涉及3個主要的東西,它們是openfire、smack和spark,這個三個東東結合起來就是完整的xmpp IM實現,這裏簡單介紹一下這3個東東在下文的作用:
openfire主要是作爲服務器,負責管理客戶端的通信連接,以及提供客戶端一些通信信息和連接信息。
Smack主要是xmpp協議的實現,提供了一套很好的api,所以下面操作xmpp都是通過使用smack的api來實現,當然因爲是在android裏,所以使用的是asmack這個包,裏面方法跟smack包差不多。
Spark 是IM客戶端的實現,其實就是使用了smack 的api實現的。
下圖展示了三者之間的關係:(很明顯這個圖是偷別人的,具體是哪裏我忘了,因爲資料都是複製到文檔後慢慢研究看的)
從圖上可以瞭解到,client 端和server端都可以通過插件的方式來進行擴展,smack是二者傳遞數據的媒介。
配置openfire服務器
具體步驟請移步:http://javatech.blog.163.com/blog/static/1766322992010111725339587/
配置成功如果以後ip地址變了,那肯定又是開不了,解決辦法請移步:http://blog.csdn.net/HappySheepherder/article/details/4707124
配置成功後,在服務器創建一個簡單的用戶來測試,然後安裝spark,設置好服務器的ip與端口,使用剛纔創建的用戶登錄,登錄OK說明服務器成功搭建。
Android IM功能(因爲是測試demo,因此界面超級簡陋,代碼都是給出重要的一部分,剩餘的可以在最後下面項目查看)
配置要求
android 2.2、 asmack-jse.jar、myeclipse
連接服務器
在打開軟件後會開始初始化,完成與openfire服務器的連接,設置一些配置
<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">static </span><span style="margin: 0px; padding: 0px; color: black;">{ XMPPConnection.</span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">DEBUG_ENABLED </span><span style="margin: 0px; padding: 0px; color: black;">= </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">true</span><span style="margin: 0px; padding: 0px; color: black;">; </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">final </span><span style="margin: 0px; padding: 0px; color: black;">ConnectionConfiguration connectionConfig = </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span><span style="margin: 0px; padding: 0px; color: black;">ConnectionConfiguration( </span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">host</span><span style="margin: 0px; padding: 0px; color: black;">, 5222, </span><span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">""</span><span style="margin: 0px; padding: 0px; color: black;">); </span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// Google talk // ConnectionConfiguration connectionConfig = new // ConnectionConfiguration( // "talk.google.com", 5222, "gmail.com");</span><span style="margin: 0px; padding: 0px; color: black;"></span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"> // connectionConfig.setSASLAuthenticationEnabled(false); </span><span style="margin: 0px; padding: 0px; color: black;">ActivityMain.</span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">connection </span><span style="margin: 0px; padding: 0px; color: black;">= </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span><span style="margin: 0px; padding: 0px; color: black;">XMPPConnection(connectionConfig); ActivityMain.</span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">connection</span><span style="margin: 0px; padding: 0px; color: black;">.</span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">DEBUG_ENABLED </span><span style="margin: 0px; padding: 0px; color: black;">= </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">true</span><span style="margin: 0px; padding: 0px; color: black;">; ProviderManager pm = </span><span style="margin: 0px; padding: 0px; color: blue;">ProviderManager</span><span style="margin: 0px; padding: 0px; color: black;">.getInstance(); configure(pm); } </span>
註冊模塊
註冊有兩種方法:一種是用createAccount ,不過我測試了一下發現不能創建用戶,具體原因不詳,下面介紹第二種。
如上圖:註冊成功後服務器將多了ggg用戶。
具體實現如下:
Registration reg = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span><span style="margin: 0px; padding: 0px; color: blue;">Registration</span>(); reg.setType(IQ.Type.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">SET</span>); reg.setTo(ConnectionSingleton.getInstance().getServiceName()); reg.setUsername(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">username</span>.getText().toString()); reg.setPassword(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">password</span>.getText().toString()); reg.addAttribute(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"android"</span>, <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"geolo_createUser_android"</span>); System.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">out</span>.println(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"reg:" </span>+ reg); PacketFilter filter = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>AndFilter(<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>PacketIDFilter(reg .getPacketID()), <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>PacketTypeFilter(IQ.<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">class</span>)); PacketCollector collector = ConnectionSingleton.getInstance() .createPacketCollector(filter); ConnectionSingleton.getInstance().sendPacket(reg); <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">result </span>= (IQ) collector.nextResult(SmackConfiguration .getPacketReplyTimeout()); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// Stop queuing results </span>collector.cancel();<span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"> </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">result </span>== <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">null</span>) { Toast.makeText(getApplicationContext(), <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"服</span>, Toast.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">LENGTH_SHORT</span>).show(); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">else if </span>(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">result</span>.getType() == IQ.Type.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">ERROR</span>) { <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">result</span>.getError().toString().equalsIgnoreCase( <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"conflict(409)"</span>)) { Toast.makeText(getApplicationContext(), <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"這</span>, Toast.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">LENGTH_SHORT</span>).show(); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">else </span>{ Toast.makeText(getApplicationContext(), <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"注</span>, Toast.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">LENGTH_SHORT</span>).show(); } } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">else if </span>(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">result</span>.getType() == IQ.Type.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">RESULT</span>) { Toast.makeText(getApplicationContext(), <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"恭</span>, Toast.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">LENGTH_SHORT</span>).show(); }
使用註冊類,設置好註冊的用戶名密碼和一些屬性字段,直接設置包過濾,根據這個過濾創建一個結果集合,發送註冊信息包,等待獲取結果,剩餘就是判斷結果內容.
登錄模塊
登錄比較簡單
ConnectionSingleton.getInstance().connect();<span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// connect </span>String account = <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">etUsername</span>.getText().toString(); String password = <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">etPassword</span>.getText().toString(); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// 保存用戶和密碼</span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"></span>ActivityLogin.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">util</span>.saveString(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">ACCOUNT_KEY</span>, account); ActivityLogin.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">util</span>.saveString(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">PASSWORD_KEY</span>, password); ConnectionSingleton.getInstance().login(account, password);<span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// login // login success </span>System.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">out</span>.println(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"login success"</span>); ActivityLogin.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">mCurrentAccount </span>= account; System.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">out</span>.println(ConnectionSingleton.getInstance() .getUser()); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// 登錄成功後發現在線狀態</span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"></span>Presence presence = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>Presence(Presence.Type.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">available</span>); ConnectionSingleton.getInstance().sendPacket(presence); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// 開始主界面</span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"></span>Intent intent = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>Intent(ActivityLogin.<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">this</span>, ActivityMain.<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">class</span>); startActivity(intent);
獲取聯繫人模塊(ActivityMain 主界面)
獲取聯繫人並將相關信息保存到一個list數組裏,最後通知listview更新界面
<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">roster </span><span style="margin: 0px; padding: 0px; color: black;">= ActivityMain.</span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">connection</span><span style="margin: 0px; padding: 0px; color: black;">.getRoster(); </span><pre class="code" style="margin: 0px 40px 0px 20px; padding: 5px; border: 1px solid rgb(192, 192, 192); border-image: none; font-family: Verdana,Helvetica,微軟雅黑,Arial,宋體,sans-serif; white-space: pre-wrap; -ms-word-wrap: break-word; background-color: rgb(251, 237, 187);" name="code"><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);"></span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public void </span>updateRoster() { Collection<RosterEntry> entries = <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">roster</span>.getEntries(); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">for </span>(RosterEntry entry : entries) { System.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">out</span>.print(entry.getName() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" - " </span>+ entry.getUser() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" - " </span>+ entry.getType() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" - " </span>+ entry.getGroups().size()); Presence presence = <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">roster</span>.getPresence(entry.getUser()); System.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">out</span>.println(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" - " </span>+ presence.getStatus() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" - " </span>+ presence.getFrom()); User user = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>User(); user.setName(entry.getName()); user.setUser(entry.getUser()); user.setType(entry.getType()); user.setSize(entry.getGroups().size()); user.setStatus(presence.getStatus()); user.setFrom(presence.getFrom()); <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">userinfos</span>.add(user); } <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">rosterAdapter</span>.notifyDataSetChanged(); }
單人聊天模塊
第一次修改:
在主界面點擊選擇一個用戶,進入聊天Activity,ActivityChat先獲取傳過來的用戶,創建聊天類並對該用戶設置消息監聽
ChatManager chatmanager = ConnectionSingleton.getInstance() .getChatManager(); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// get user </span>Intent intent = getIntent(); String user = intent.getStringExtra(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"user"</span>); System.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">out</span>.println(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"user:" </span>+ user); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// new a session </span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">newChat </span>= chatmanager.createChat(user, <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">null</span>); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// 監聽聊天消息</span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"> </span>chatmanager.addChatListener(<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>ChatManagerListenerEx()); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// send message </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">try </span>{ <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">newChat</span>.sendMessage(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"im bird man"</span>); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">catch </span>(XMPPException e) { <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// </span><span style="margin: 0px; padding: 0px; color: rgb(127, 159, 191);">TODO </span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">Auto-generated catch block </span>e.printStackTrace(); }
監聽類
<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public class </span>ChatManagerListenerEx <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">implements </span>ChatManagerListener { <span style="margin: 0px; padding: 0px; color: rgb(100, 100, 100);">@Override </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public void </span>chatCreated(Chat chat, <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">boolean </span>arg1) { <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// </span><span style="margin: 0px; padding: 0px; color: rgb(127, 159, 191);">TODO </span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">Auto-generated method stub </span>chat.addMessageListener(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">ml</span>); } } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public class </span>MessageListenerEx <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">implements </span>MessageListener { <span style="margin: 0px; padding: 0px; color: rgb(100, 100, 100);">@Override </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public void </span>processMessage(Chat arg0, Message message) { String result = message.getFrom() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">":" </span>+ message.getBody(); System.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">out</span>.println(result); android.os.Message msg = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>android.os.Message(); msg.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">what </span>= 0; Bundle bd = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>Bundle(); bd.putString(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"msg"</span>, result); msg.setData(bd); <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">handler</span>.sendMessage(msg); } }
所獲取到的消息都是通過handler來更新UI
<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public </span>Handler <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">handler </span>= <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>Handler() { <span style="margin: 0px; padding: 0px; color: rgb(100, 100, 100);">@Override </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public void </span>handleMessage(android.os.Message msg) { <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">switch </span>(msg.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">what</span>) { <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">case </span>0: { String result = msg.getData().getString(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"msg"</span>); <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">record</span>.setText(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">record</span>.getText() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"\n" </span>+ result); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">break</span>; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">default</span>: <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">break</span>; } } };
aaa跟bbb 的聊天
第二次修改:
第一次的測試,發現如果多個人之間都成爲好友,那麼他們之間的聊天就出現了接收不到的信息,當然在跟spark測試時,spark可以收到android端的信息,不過android客戶端是收到這個信息,不過卻沒有顯示出來,具體原因還不太清楚。因此在第二次修改我改成監聽所有聊天信息包,然後再分析包的歸屬,分發到對應的聊天窗口。
這裏就是監聽到包後打印的消息,打印出了jid和消息內容
<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public class </span>XmppMessageManager <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">implements </span>ChatManagerListener { <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">private </span>XMPPConnection _connection; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">private </span>ChatManager manager = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">null</span>; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public void </span>initialize(XMPPConnection connection) { _connection = connection; manager = _connection.getChatManager(); manager.addChatListener(<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">this</span>); } <span style="margin: 0px; padding: 0px; color: rgb(100, 100, 100);">@</span>Override <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public void </span>chatCreated(Chat chat, <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">boolean </span>arg1) { <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// </span><span style="margin: 0px; padding: 0px; color: rgb(127, 159, 191);">TODO </span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">Auto-generated method stub </span>chat.addMessageListener(<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>MessageListener() { <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public void </span>processMessage(Chat newchat, Message message) { <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// 若是聊天窗口存在,將消息轉往目前窗口 // 若窗口不存在,創建新的窗口 </span>System.out .println(message.getFrom() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">":" </span>+ message.getBody()); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(!ActivityMain.chats.containsKey(message.getFrom())) { ActivityMain.chats.put(message.getFrom(), newchat); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">else </span>{ } } }); } }
主要就是重寫了ChatManagerListener類的監聽,分發處理暫時沒有想好怎麼寫。接着在後臺啓動service就可以開始監聽,行了第一次修改那些可以去掉了^0^。
多人聊天模塊
也是在主界面的菜單進入ActivityMultiChat,該界面顯示所創建的房間,點擊就跳轉到ActivityMultiRoom 。
獲取所有房間比較簡單,只需執行下面這段代碼
hostrooms = MultiUserChat.getHostedRooms(ActivityMain.connection,
"conference.zhanghaitao-pc");
跳轉到後獲取要加入的房間的jid,並創建監聽。
jid = getIntent().getStringExtra("jid");
<span style="margin: 0px; padding: 0px; color: rgb(0, 255, 0);"><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"> //</span>後面服務名稱必需是創建房間的那個服務</span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"> </span>String multiUserRoom = jid; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">try </span>{ muc = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>MultiUserChat(ActivityMain.connection, <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">multiUserRoom</span>); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">//<span style="margin: 0px; padding: 0px; color: rgb(0, 255, 64);"> </span></span><span style="margin: 0px; padding: 0px; color: rgb(0, 255, 64);">創建聊天室,進入房間後的nickname</span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);"> </span>muc.join(ActivityLogin.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">mCurrentAccount</span>); Log.v(TAG, <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"join success"</span>); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">catch </span>(XMPPException <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">e</span>) { <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// </span><span style="margin: 0px; padding: 0px; color: rgb(127, 159, 191);">TODO </span><span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">Auto-generated catch block </span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">e</span>.printStackTrace(); } ChatPacketListener <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">chatListener </span>= <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span><span style="margin: 0px; padding: 0px; color: blue;">ChatPacketListener</span>(muc); muc.addMessageListener(chatListener);
監聽大概的流程跟單人聊天差不多,都是handler來操作。不過多人聊天是重寫了PacketListener。具體如下(不過該方法是監聽房間的信息,也就是說顯示的是以房間爲名字的消息):
<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">class </span>ChatPacketListener <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">implements </span>PacketListener { <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">private </span>String <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">_number</span>; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">private </span>Date <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">_lastDate</span>; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">private </span>MultiUserChat <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">_muc</span>; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">private </span>String <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">_roomName</span>; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public </span>ChatPacketListener(MultiUserChat muc) { <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">_number </span>= <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"0"</span>; <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">_lastDate </span>= <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>Date(0); <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">_muc </span>= muc; <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">_roomName </span>= muc.getRoom(); } <span style="margin: 0px; padding: 0px; color: rgb(100, 100, 100);">@Override </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">public void </span>processPacket(Packet packet) { Message message = (Message) packet; String from = message.getFrom(); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(message.getBody() != <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">null</span>) { DelayInformation inf = (DelayInformation) message.getExtension( <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"x"</span>, <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"jabber:x:delay"</span>); Date sentDate; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(inf != <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">null</span>) { sentDate = inf.getStamp(); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">else </span>{ sentDate = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>Date(); } Log.w(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">TAG</span>, <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"Receive old message: date=" </span>+ sentDate.toLocaleString() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" ; message=" </span>+ message.getBody()); android.os.Message msg = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>android.os.Message(); msg.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">what </span>= <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">RECEIVE</span>; Bundle bd = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>Bundle(); bd.putString(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"from"</span>, from); bd.putString(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"body"</span>, message.getBody()); msg.setData(bd); <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">handler</span>.sendMessage(msg); } } }
下載模塊
在主界面對着用戶名長按,進入下載activity。進入activityFileTransfer,點擊傳輸按鈕即可將文件傳輸給之前選擇的用戶,當然這裏做得比較簡單,並沒有拒絕功能,一旦發現有文件就接受。
<span style="margin: 0px; padding: 0px; color: black;">FileTransferManager transfer = </span><span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span><span style="margin: 0px; padding: 0px; color: black;">FileTransferManager( ActivityMain.</span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">connection</span><span style="margin: 0px; padding: 0px; color: black;">); String destination = </span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">user</span><span style="margin: 0px; padding: 0px; color: black;">; OutgoingFileTransfer out = transfer .createOutgoingFileTransfer(destination + </span><span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"/Smack"</span><span style="margin: 0px; padding: 0px; color: black;">); </span>
那用戶是如何監聽到有文件並且接受呢?在進入主界面的時候就已經開始了一個service(fileListenerService),該服務創建文件的監聽類(XmppFileManager),監聽類主要繼承FileTransferListener 重寫裏面的fileTransferRequest方法。
File saveTo; <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// set answerTo for replies and send() </span><span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">answerTo </span>= request.getRequestor(); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(!Environment.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">MEDIA_MOUNTED</span>.equals(Environment .getExternalStorageState())) { send(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"External Media not mounted read/write"</span>); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">return</span>; } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">else if </span>(!<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">landingDir</span>.isDirectory()) { send(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"The directory " </span>+ <span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">landingDir</span>.getAbsolutePath() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" is not a directory"</span>); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">return</span>; } saveTo = <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">new </span>File(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">landingDir</span>, request.getFileName()); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(saveTo.exists()) { send(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"The file " </span>+ saveTo.getAbsolutePath() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" already exists"</span>); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// delete </span>saveTo.delete(); <span style="margin: 0px; padding: 0px; color: rgb(63, 127, 95);">// return; </span>} IncomingFileTransfer transfer = request.accept(); send(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"File transfer: " </span>+ saveTo.getName() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" - " </span>+ request.getFileSize() / 1024 + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" KB"</span>); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">try </span>{ transfer.recieveFile(saveTo); send(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"File transfer: " </span>+ saveTo.getName() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" - " </span>+ transfer.getStatus()); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">double </span>percents = 0.0; <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">while </span>(!transfer.isDone()) { <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(transfer.getStatus().equals(Status.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">in_progress</span>)) { percents = ((<span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">int</span>) (transfer.getProgress() * 10000)) / 100.0; send(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"File transfer: " </span>+ saveTo.getName() + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">" - " </span>+ percents + <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"%"</span>); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">else if </span>(transfer.getStatus().equals(Status.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">error</span>)) { send(returnAndLogError(transfer)); <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">return</span>; } Thread.sleep(1000); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">if </span>(transfer.getStatus().equals(Status.<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">complete</span>)) { send(<span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"File transfer complete. File saved as " </span>+ saveTo.getAbsolutePath()); } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">else </span>{ send(returnAndLogError(transfer)); } } <span style="margin: 0px; padding: 0px; color: rgb(127, 0, 85);">catch </span>(Exception ex) { String message = <span style="margin: 0px; padding: 0px; color: rgb(42, 0, 255);">"Cannot receive the file because an error occured during the process." </span>+ ex; Log.e(<span style="margin: 0px; padding: 0px; color: rgb(0, 0, 192);">TAG</span>, message, ex); send(message); }
網上說文件的傳輸遇到幾個比較重要的問題,我也查看了很多資料(國內的基本是寥寥無幾,對此我感到挺無奈的,只能看國外,這樣每次我的英語水平都提高了太無奈了^0^)。在這個android asmack的最新版本好像是解決了幾個比較重要的問題,剩下一個傳輸文件沒反應的問題我在初始化連接時調用了configure方法解決。不過不知道是不是這個原因,後面出現了一個神奇的問題,就是有時可以成功傳輸有時傳輸時客戶端沒響應(如果有人完美解決了這個問題,那就………趕緊將代碼放出來一起共享^-^,以提高我國程序員的整體水平,多偉大遙遠的理想啊~~)。
項目下載
http://files.cnblogs.com/not-code/ASmack.zip
本文爲原創翻譯,如需轉載,請註明作者和出處,謝謝!
出處:http://www.cnblogs.com/not-code/archive/2011/07/16/2108369.html
文章其實在一個星期前就寫好了, 不過一直忙着看asmack源碼,發現有很多可以學習的地方,可以自己定製自己喜歡的類和功能,因此越到後面越發現自己這篇文章很多地方都寫得挺簡單的,差點就不想發了^-^。不過爲了上面那個偉大的理想……