步驟4:好友清單以及狀态(Roster and Presence)。
Roster:好友清單,但它不是一個表,而是一個比較複雜的類。
Presence:好友的狀态,是Roster中包含的類,但有特殊作用。
大家閱讀源碼,了解Roster 與 Presence包含的資訊。特别注意幾個枚舉類的不同,不要混淆了。
·加載清單:
通過Roster類的
getInstanceFor()
函數和connection就可以獲得執行個體。通過
getEntries
就可以獲得所有Entry,即是獲得好友清單資訊。
通過
getPresence()
函數可以獲得Presence。
·監聽清單:
通過
addRosterListener()
添加監聽器,則可以監聽好友的資訊的變化,如上線下線等。
Roster參考代碼:
roster = Roster.getInstanceFor(connection);
roster.setSubscriptionMode(Roster.SubscriptionMode.manual);
roster.addRosterListener(new RosterListener() {
@Override
public void entriesAdded(Collection<String> addresses) {
for(String jid : addresses){
RosterEntry entry= roster.getEntry(jid);
entries.put(jid,entry);
}
rosterChangedListener.rosterChanged();
}
@Override
public void entriesUpdated(Collection<String> addresses) {
rosterChangedListener.rosterChanged();
}
@Override
public void entriesDeleted(Collection<String> addresses) {
for(String jid : addresses){
entries.remove(jid);
rosterChangedListener.rosterChanged();
}
}
@Override
public void presenceChanged(Presence presence) {
presenceHashMap.put(presence.getFrom(),presence);
rosterChangedListener.rosterChanged();
}
});
!注意!
當你直接寫了代碼并運作後,可能會發現Roster的Entries會是空的!這個問題筆者網上找了許久,最後發現是由于用戶端運作過快,在登入後馬上擷取Roster,導緻Roster内容為空,或則說在伺服器還沒有把Roster傳過來時,代碼已經運作完畢了。解決方法是在登入後将線程暫停一下,再擷取一次Roster。
步驟5:多人聊天
多人聊天與單人聊天不同,其是基于XEP協定的,差別之處在于多人聊天要在伺服器上建立聊天室,然後其他加入聊天室中聊天,而單人聊天則隻是兩個節點之間的對話。其實對于這裡的設計有點不太了解,因為這樣使用兩套設計令到實作用戶端時需要進行兩套實作,然而這兩種聊天方式是有很多共性的,因而會導緻很多的重複。我看了看微信的實作方式,微信應該是将兩種聊天方式統一起來的,單人聊天中可以随時添加其他人,也就是說隻使用了XMPP中多人聊天方式這一種實作。
多人聊天中重要的類有:
MultiUserChat:類似單人聊天的Chat,是多人聊天會話類。
MultiUserChatManager:多人聊天會話的管理類。
接口:
MessageListener:消息監聽接口
多人聊天比單人聊天的建立複雜一些,多了建立、發送邀請、加入聊天室三個步驟。
參考代碼:
加入聊天室
public void joinChattingRoom(MultiUserChat multiUserChat,String nickname) throws SmackException.NotConnectedException, XMPPException.XMPPErrorException, SmackException.NoResponseException {
multiUserChat.join(nickname);
dataManager.addToChatsList(multiUserChat.getRoom());
chatsMap.put(multiUserChat.getRoom(), multiUserChat);
multiUserChat.addMessageListener(new MessageListener() {
@Override
public void processMessage(Message message) {
if(message.getType().equals(Message.Type.groupchat)){
dataManager.processMessage(message);
}
}
});
}
邀請監聽
multiUserChatManager.addInvitationListener(new InvitationListener() {
@Override
public void invitationReceived(XMPPConnection xmppConnection, MultiUserChat multiUserChat, String inviter, String reason, String s3, Message message) {
//回調一下activity,彈框
invitationReceivedListener.onInvitationReceived(multiUserChat, inviter , reason);
}
});
建立聊天室
public void createMultiUserChat(String roomName,String nickname) throws XMPPException.XMPPErrorException, SmackException {
String jid = roomName+"@conference.192.168.0.101";
MultiUserChat muc = multiUserChatManager.getMultiUserChat(jid);
muc.create(nickname);
dataManager.addToChatsList(jid);
chatsMap.put(jid, muc);
muc.sendConfigurationForm(new Form(DataForm.Type.submit));
muc.addMessageListener(new MessageListener() {
@Override
public void processMessage(Message message) {
if (message.getType().equals(Message.Type.groupchat)) {
dataManager.processMessage(message);
}
}
});
}
以上就是實作XMPP聊天用戶端的核心内容,其他額外的實作就多說,畢竟本人較菜,處于學習中,不值得參考。
項目小結:
基本上隻是簡單地實作了一個文本通信的簡單程式,收獲也頗多。
首先,學會了基于開源的庫怎麼去開發,流程大概是:
讀理論——>讀庫的開發文檔(邊度邊動手)——>讀源碼——>設計子產品——>實作設計。
第二,當程式寫着寫着,發現資料異常亂,原因是沒有将資料剝離出來,後來準備寫一個資料層,寫了一些,發現資料層不好寫,原因是資料類型有多種,以及某些類的耦合性導緻資料剝離會重複做工,例如Roster類,包含了資料。後來大緻看了一下Spark的設計,發現它不是分出資料層,而是将每一種資料設計一個容器,感覺這個思路比集中一個資料層要好一點。
第三,在架構方面比以前小有長進,對比了Spark的架構,發現設計的思路較為類似,都是UI分一層,利用管理類作為控制。
第四,當然就是熟悉了部分網絡程式設計的知識了,了解了一些協定之間的差別。