(一)客戶端與服務器建立連接
上一篇寫到ClientSession createClientSession這裡,創建一個客戶端的session。在SessionManager類中創建了session之後,這裡拼接了兩個xml內容的text。一個是Build the start packet response,創建一個頭條包,作為回應。另外一個是:XMPP 1.0 needs stream features,是xmpp1.0所需要的文件結構。兩個消息的格式內容如下:
<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="127.0.0.1"
id="bdef9c6a" xml:lang="en" version="1.0">
<stream:features>
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls>
<auth xmlns="http://jabber.org/features/iq-auth"/>
<register xmlns="http://jabber.org/features/iq-register"/>
</stream:features>
然後,調用connection的deliverRawText方法,將這兩個xml內容通過IOSession的writer方法,傳輸到mina裡面。具體mina怎麼處理,本人還沒有研究過。
到此,根據記錄的log日志,此消息已經發布了。客戶端與服務器建立連接的過程,這裡已經完成。這裡只是一部分,關於客戶端消息的發送,這部分內容也應該不少。
(二)服務器推送消息
從服務器推送消息的時候,會調用NotificationManager類裡面的方法,分別為廣播和對單個用戶發送。這裡主要看廣播。
首先是構造IQ消息體,createNotificationIQ方法來構造。
復制代碼
/**
* Creates a new notification IQ and returns it.
* 構造消息體格式
*/
private IQ createNotificationIQ(String apiKey, String title,
String message, String uri) {
Random random = new Random();
String id = Integer.toHexString(random.nextInt());
// String id = String.valueOf(System.currentTimeMillis());
Element notification = DocumentHelper.createElement(QName.get(
"notification", NOTIFICATION_NAMESPACE));
notification.addElement("id").setText(id);
notification.addElement("apiKey").setText(apiKey);
notification.addElement("title").setText(title);
notification.addElement("message").setText(message);
notification.addElement("uri").setText(uri);
IQ iq = new IQ();
iq.setType(IQ.Type.set);
iq.setChildElement(notification);
return iq;
}
復制代碼
構造後的xml:
復制代碼
<iq type="set" id="860-0" to="
[email protected]/AndroidpnClient">
<notification xmlns="androidpn:iq:notification">
<id>11218d6c</id>
<apiKey>1234567890</apiKey>
<title>你好</title>
<message>你好啊</message>
<uri></uri>
</notification>
</iq>
復制代碼
在ClientSession中查找指定用戶名的session,如果存在,則發送此條IQ消息。調用connectin的deliver方法,通過ioSession.write(buffer),將xml信息傳輸給mina,並且將發送記錄加1。
客戶端源碼分析:
客戶端代碼很簡單的,依靠xmppmanager維持連接。這裡說一下大概流程。
當客戶端推送消息過來的時候,NotificationReceiver類的onReceive方法接收到消息,在這裡,這時候,已經獲得了所有發過來的數據。在這裡,已經可以做自己的事情了,因為已經有了需要的數據。不過貌似挺多人喜歡在notifier的notify方法中來進行處理。
其他就是自己的業務了。
還有一個是關於客戶端的用戶名和密碼,這個默認是自動生成,也可以自動指定。在XMPPManager的run方法裡面可以修改。修改後會出現一些問題,就是服務器端注冊的時候,會出現用戶名已經存在,重復插入的問題。這個需要在服務器端插入數據的時候修改一下代碼,在UserServiceImpl中修改,為了業務需要,本人把hibernate修改為了mybatis,所以方法略有不同:
復制代碼
public User saveUser(User user) throws UserExistsException {
try {
//修改為自己的用戶登錄
try {
user=getUserByUsername(user.getUsername());
} catch (UserNotFoundException e) {
// TODO Auto-generated catch block
log.info(user.getUsername()+" is not exist in db ....");
userDao.saveUser(user);
e.printStackTrace();
}
} catch (DataIntegrityViolationException e) {
e.printStackTrace();
log.warn(e.getMessage());
throw new UserExistsException("User '" + user.getUsername()
+ "' already exists!");
} catch (EntityExistsException e) { // needed for JPA
e.printStackTrace();
log.warn(e.getMessage());
throw new UserExistsException("User '" + user.getUsername()
+ "' already exists!");
}
return user;
}
復制代碼
這樣就可以了。
關於短線重連,網上也有很多解決方法,是客戶端加一行代碼:
復制代碼
private void addTask(Runnable runnable) {
Log.d(LOGTAG, "addTask(runnable)...");
taskTracker.increase();
synchronized (taskList) {
if (taskList.isEmpty() && !running) {
running = true;
futureTask = taskSubmitter.submit(runnable);
if (futureTask == null) {
taskTracker.decrease();
}
} else {
//解決服務器端重啟後,客戶端不能成功連接androidpn服務器
runTask();
taskList.add(runnable);
}
}
Log.d(LOGTAG, "addTask(runnable)... done");
}
復制代碼
當然,其他還有許多細節,先到這裡,感謝網上那麼多博主寫的一些資料。