圖文無關一起娛樂:
這一篇我們開始寫Android端的Smack版主類,后面Android的IM功能都是通過這個幫助類實現的
引用類庫:
因為我用的是IDE是Android Studio,所以我通過gradle進行jar包管理了,非常方便,jar包如下:
compile 'org.igniterealtime.smack:smack-core:4.1.4' compile 'org.igniterealtime.smack:smack-tcp:4.1.4' compile 'org.igniterealtime.smack:smack-extensions:4.1.4' compile 'org.igniterealtime.smack:smack-android:4.1.4' compile 'org.igniterealtime.smack:smack-android-extensions:4.1.4' compile 'org.igniterealtime.smack:smack-experimental:4.1.4' compile 'org.igniterealtime.smack:smack-bosh:4.1.4' compile 'org.igniterealtime.smack:smack-resolver-dnsjava:4.1.4' compile 'org.igniterealtime.smack:smack-legacy:4.1.4'
加上這些jar包后我們就可以使用Smack庫了,大家很容易看到,我這個是4.1.4的包。它的最新包是4.2.0不過是alpha版本,所以我就用這個4.1.4最新正式包了。它和它的一些老版本差別還是比較大的。所以如果你是第一次使用還是和我一樣,防止出現一些問題。更新gradle后就可以使用了。
寫Smack幫助類:
我這里先定義一個ISmack接口,這種命名方式是我從C#那邊帶過來的,別糾結。本人很久之前是.NET平台的,.NET平台還是有很多東西是非常不錯的。這個接口就是一些約束協議。然后再寫了一個Smack 類,它繼承並且實現了ISmack接口。
ISmack接口代碼如下:
/** * * @備注:samck操作接口協議 * @作者:高露 * @時間:2015-10-24 * @QQ:408365330 * */ public interface ISmack { /** * 登錄 * @param name 賬號 * @param pwd 密碼 * @return */ public boolean login(String name,String pwd); /** * 消息發送 * @param from 誰發送 * @param to 發送給誰 * @param content 發送內容 */ public void sendMessage(String from,String to,String content) throws SmackException.NotConnectedException; /** *添加好友 * @param user 用戶jid * @param name 添加好友備注名稱 * @param groups 好友添加到的分組,可以過個組 */ public void addRosterItem(String user,String name,String[] groups) throws Exception; /** * 刪除好友 * @param user 好友jid */ public void removeRoster(String user); }
可以通過這份協議知道我這里定義了 登錄,消息發送,添加好友,刪除好友 這些方法,后面還是會添加的,每個方法備注還是比較詳細的,看看就知道是什么意思了。
然后我們再看看和分析Smack代碼
Smack代碼分析:
靜態構造函數:
首先定義了一個java靜態構造函數
static { if (connection == null) { XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder() .setConnectTimeout(Constant.IM_TIMEOUT) .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled) .setHost(Constant.IM_SERVER)//ip .setPort(Constant.IM_SERVER_PORT)//端口號設置一般式5222 .setServiceName(Constant.IM_SERVER_DOMAIN)//服務器名稱 .setDebuggerEnabled(true)//設置開啟調試 .setSendPresence(true)//設置是否發送Presece信息 .build(); connection = new XMPPTCPConnection(configuration); connection.addConnectionListener(new ConnectionListener() { @Override public void connected(XMPPConnection connection) { LogHelper.i(TAG, "connected"); } @Override public void authenticated(XMPPConnection connection, boolean resumed) { LogHelper.i(TAG, "authenticated"); } @Override public void connectionClosed() { LogHelper.i(TAG, "connectionClosed"); } @Override public void connectionClosedOnError(Exception e) { LogHelper.i(TAG, "connectionClosedOnError"); } @Override public void reconnectionSuccessful() { LogHelper.i(TAG, "reconnectionSuccessful"); } @Override public void reconnectingIn(int seconds) { LogHelper.i(TAG, "reconnectingIn"); } @Override public void reconnectionFailed(Exception e) { LogHelper.i(TAG, "reconnectionFailed"); } }); ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection); reconnectionManager.setFixedDelay(Constant.IM_RE_CONNET_TIME);//重聯間隔 reconnectionManager.enableAutomaticReconnection();//開啟重聯機制 try { connection.connect(); } catch (SmackException | IOException | XMPPException ex) { ex.printStackTrace(); } } }
XMPPTCPConnectionConfiguration類是連接的配置,連接相關配置都是通過這個類傳遞給XMPPTCPConnection對象的。配置信息的設置請看上面的代碼注釋,connection.addConnectionListener是給連接添加監聽,這里面我們可以監聽連接的各種狀態。然后做相應的處理。
ReconnectionManager這個類是重聯管理設置連接斷開后是否允許重新連接。這樣實現自動重聯
最后通過connection.connect()連接XMPP服務器
登錄:
/** * 登錄 * @param name 賬號 * @param pwd 密碼 * @return */ @Override public boolean login(String name, String pwd) { try { if (connection.isConnected()) { connection.login(name, pwd); } else { connection.connect(); connection.login(name, pwd); } } catch (XMPPException | SmackException | IOException ex) { ex.printStackTrace(); } /** * 消息監聽 */ registerMessageListener(); /** * 通訊錄監聽 */ registerRosterListener(); return false; }
通過用戶名和密碼進行登錄。登錄之前判斷是否連接了XMPP服務器。登錄后添加消息監聽處理消息,通訊錄監聽處理通訊錄。下面再看看這兩個監聽方法
通訊錄監聽:
/** * 通訊錄監聽 */ private void registerRosterListener() { Roster roster = Roster.getInstanceFor(connection); roster.setSubscriptionMode(Roster.SubscriptionMode.manual);//設置添加好友,需要對方確認 roster.addRosterListener(new RosterListener() { @Override public void entriesAdded(Collection<String> collection) { LogHelper.i(TAG, "通訊錄用戶添加"); } @Override public void entriesUpdated(Collection<String> collection) { LogHelper.i(TAG, "通訊錄用戶變更"); } @Override public void entriesDeleted(Collection<String> collection) { LogHelper.i(TAG, "通訊錄用戶刪除"); } @Override public void presenceChanged(Presence presence) { LogHelper.i(TAG, "通訊錄用戶presence變化"); } }); }
Roster類管理這通訊錄。給通訊錄添加了監聽這樣可以處理通訊里,請看上面的代碼。這里重點講解roster.setSubscriptionMode(Roster.SubscriptionMode.manual);這個設置通訊里添加好友的模式。這里設置了添加好友需要確認,而不是直接成為好友,一般都是這樣,但是這里也支持直接成為好友。我們通過源碼來看看SubscriptionMode這個枚舉。
/** * 好友請求訂閱的模式枚舉. */ public enum SubscriptionMode { /** * 自動接收所有好友請求. This is * 這是默認的模式,適合簡單的客戶端. 更復雜的客戶端希望手動處理好友添加請求. */ accept_all, /** * 自動拒絕所有請求 */ reject_all, /** * 好友請求被忽略,意味着必須手動注冊和處理presence監聽,然后處理類型是 * Presence.Type.SUBSCRIBE類型或者是Presence.Type.UNSUBSCRIBE類型的 presence包 */ manual }
以上是我通過源碼中的英文轉譯的,鄙人英文就這水平見諒,見諒。這里說的presence包不懂的話看看第一篇XMPP協議簡析,這樣就懂了。我的原則是任何東西先懂原理(先修煉好《易筋經》然后再學招式,否則累也記不住,不懂硬記很累,有些學編程,語言沒學好然后直接上平台性的東西,發現容易出問題,同樣一個道理)。
消息監聽:
/** * 各種消息包監聽 */ private void registerMessageListener() { connection.addSyncStanzaListener(new StanzaListener() { @Override public void processPacket(Stanza stanza) throws SmackException.NotConnectedException { if (stanza instanceof Message) {//表示接收到是消息包 Message message = (Message) stanza; if (message.getType() == Message.Type.chat) {//表示單聊 } if (message.getType() == Message.Type.groupchat) {//表示群聊 } if (message.getType() == Message.Type.error) {//表示錯誤信息 } } if (stanza instanceof Presence) {//表示接收到的是Presence包 } if (stanza instanceof IQ) {//表示接收到的是IQ包 } } }, new StanzaFilter() { @Override public boolean accept(Stanza stanza) { return true; } });
雖然還有其它監聽消息的方法,但是我選擇這種了,因為這種可以監聽所有各種消息包,看上面注釋能理解。如果不懂這些包神馬意思還是推薦你看前面第一篇XMPP協議簡析。那么久知道神馬是IQ,神馬是Message,什么是Presence包了
new StanzaFilter() { @Override public boolean accept(Stanza stanza) { return true; } }
這個是過濾包,我這里返回ture就是不過濾直接接受所有XMPP包。你還可以過濾特定的包比如IQ包 StanzaFilter filter=new StanzaTypeFilter(IQ.class) ,然后把這個作為 addSyncStanzaListener方法的第二個參數這樣就只能接受IQ消息了。
消息發送:
/** * 消息發送 * * @param from 誰發送 * @param to 發送給誰 * @param content 發送內容 */ @Override public void sendMessage(String from, String to, String content) throws SmackException.NotConnectedException { Message msg = new Message(to, content); msg.setFrom(from); connection.sendStanza(msg); // ChatManager chatmanager = ChatManager.getInstanceFor(connection); // Chat newChat = chatmanager.createChat(to, new ChatMessageListener() { // @Override // public void processMessage(Chat chat, Message message) { // LogHelper.i(TAG, "接收到消息:" + message); // // } // }); // newChat.sendMessage(content); }
我這里可以看到有兩種方式,一種直接原始的發送Message包,另一種就是被我注釋的代碼段,通過ChatManager來發送消息
添加好友:
/** * 添加好友 * @param user 用戶jid * @param name 添加好友備注名稱 * @param groups 好友添加到的分組,可以過個組 * @throws Exception */ @Override public void addRosterItem(String user, String name, String[] groups) throws Exception{ Roster roster=Roster.getInstanceFor(connection); roster.createEntry(user,name,null); }
這里沒神馬好講的,因為上面設置了 roster.setSubscriptionMode(Roster.SubscriptionMode.manual);//設置添加好友,需要對方確認 這種模式,添加好友需要對方確認
最后:
今天就此結束,雖然只有這些方法,如果你結合前面第一篇XMPP協議簡介弄懂原理,我們就共同進步了:)。希望我們一起每天進步一點。
Q Q:408365330 E-Mail:egojit@qq.com