此篇文章繼上一篇物聯網協議之MQTT原始碼分析(一)而寫的第二篇MQTT釋出訊息以及接收Broker訊息的原始碼分析,想看MQTT連線的小夥伴可以去看我上一篇哦。
MQTT釋出訊息
MQTT釋出訊息是由MqttAndroidClient類的publish函式執行的,我們來看看這個函式:
// MqttAndroidClient類:
@Override
public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
boolean retained, Object userContext,
IMqttActionListener callback)
throws MqttException, MqttPersistenceException {
// 將訊息內容、qos訊息等級、retained訊息是否保留封裝成MqttMessage
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
message.setRetained(retained);
// 每一條訊息都有自己的token
MqttDeliveryTokenAndroid token = new MqttDeliveryTokenAndroid(
this, userContext, callback, message);
String activityToken = storeToken(token);
IMqttDeliveryToken internalToken = mqttService.publish(clientHandle,
topic, payload, qos, retained, null, activityToken);
token.setDelegate(internalToken);
return token;
}
複製程式碼
從上面程式碼可以看出,釋出訊息需要topic訊息主題、payload訊息內容、callback回撥監聽等,經由mqttService.publish繼續執行釋出操作:
// MqttService類:MQTT唯一元件
public IMqttDeliveryToken publish(String clientHandle, String topic,
byte[] payload, int qos, boolean retained,
String invocationContext, String activityToken)
throws MqttPersistenceException, MqttException {
MqttConnection client = getConnection(clientHandle);
return client.publish(topic, payload, qos, retained, invocationContext,
activityToken);
}
複製程式碼
MqttConnection在上一篇中講解過,MQTT的連線會初始化一個MqttConnection,並儲存在一個Map集合connections中,並通過getConnection(clientHandle)方法獲取。很明顯我們要接著看client.publish函式啦:
// MqttConnection類:
public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
boolean retained, String invocationContext,
String activityToken) {
// 用於釋出訊息,是否釋出成功的回撥
final Bundle resultBundle = new Bundle();
resultBundle.putString(MqttServiceConstants.CALLBACK_ACTION,
MqttServiceConstants.SEND_ACTION);
resultBundle.putString(MqttServiceConstants.CALLBACK_ACTIVITY_TOKEN,
activityToken);
resultBundle.putString(
MqttServiceConstants.CALLBACK_INVOCATION_CONTEXT,
invocationContext);
IMqttDeliveryToken sendToken = null;
if ((myClient != null) && (myClient.isConnected())) {
// 攜帶resultBundle資料,用於監聽回撥發布訊息是否成功
IMqttActionListener listener = new MqttConnectionListener(
resultBundle);
try {
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
message.setRetained(retained);
sendToken = myClient.publish(topic, payload, qos, retained,
invocationContext, listener);
storeSendDetails(topic, message, sendToken, invocationContext,
activityToken);
} catch (Exception e) {
handleException(resultBundle, e);
}
} else {
resultBundle.putString(MqttServiceConstants.CALLBACK_ERROR_MESSAGE,
NOT_CONNECTED);
service.traceError(MqttServiceConstants.SEND_ACTION, NOT_CONNECTED);
service.callbackToActivity(clientHandle, Status.ERROR, resultBundle);
}
return sendToken;
}
複製程式碼
這段程式碼中很明顯可以看出釋出的操作又交給了myClient.publish方法,那myClient是誰呢?上一篇文章中講過myClient是MqttAsyncClient,是在MQTT連線時在MqttConnection類的connect方法中初始化的,詳情請看上一篇。
// MqttAsyncClient類:
public IMqttDeliveryToken publish(String topic, byte[] payload, int qos
, boolean retained,Object userContext,
IMqttActionListener callback) throws MqttException,MqttPersistenceException {
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
message.setRetained(retained);
return this.publish(topic, message, userContext, callback);
}
public IMqttDeliveryToken publish(String topic, MqttMessage message
, Object userContext,
IMqttActionListener callback) throws MqttException,MqttPersistenceException {
final String methodName = "publish";
// @TRACE 111=< topic={0} message={1}userContext={1} callback={2}
log.fine(CLASS_NAME, methodName, "111", new Object[]{topic, userContext, callback});
// Checks if a topic is valid when publishing a message.
MqttTopic.validate(topic, false/* wildcards NOT allowed */);
MqttDeliveryToken token = new MqttDeliveryToken(getClientId());
token.setActionCallback(callback);
token.setUserContext(userContext);
token.setMessage(message);
token.internalTok.setTopics(new String[]{topic});
MqttPublish pubMsg = new MqttPublish(topic, message);
comms.sendNoWait(pubMsg, token);
// @TRACE 112=<
log.fine(CLASS_NAME, methodName, "112");
return token;
}
複製程式碼
從這段程式碼中可以看到,現在把把topic和message封裝成了MqttPublish型別的訊息,並繼續由comms.sendNoWait執行,comms是ClientComms,ClientComms是在初始化MqttAsyncClient的構造方法中初始化的,詳情看上一篇。
// ClientComms類:
public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttException {
final String methodName = "sendNoWait";
// 判斷狀態或者訊息型別
if (isConnected() ||
(!isConnected() && message instanceof MqttConnect) ||
(isDisconnecting() && message instanceof MqttDisconnect)) {
if (disconnectedMessageBuffer != null && disconnectedMessageBuffer.getMessageCount() != 0) {
//@TRACE 507=Client Connected, Offline Buffer available, but not empty. Adding
// message to buffer. message={0}
log.fine(CLASS_NAME, methodName, "507", new Object[]{message.getKey()});
if (disconnectedMessageBuffer.isPersistBuffer()) {
this.clientState.persistBufferedMessage(message);
}
disconnectedMessageBuffer.putMessage(message, token);
} else {
// 現在不是disconnect因此,邏輯走這裡
this.internalSend(message, token);
}
} else if (disconnectedMessageBuffer != null) {
//@TRACE 508=Offline Buffer available. Adding message to buffer. message={0}
log.fine(CLASS_NAME, methodName, "508", new Object[]{message.getKey()});
if (disconnectedMessageBuffer.isPersistBuffer()) {
this.clientState.persistBufferedMessage(message);
}
disconnectedMessageBuffer.putMessage(message, token);
} else {
//@TRACE 208=failed: not connected
log.fine(CLASS_NAME, methodName, "208");
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
}
}
void internalSend(MqttWireMessage message, MqttToken token) throws MqttException {
final String methodName = "internalSend";
...
try {
// Persist if needed and send the message
this.clientState.send(message, token);
} catch (MqttException e) {
// 注意此處程式碼***
if (message instanceof MqttPublish) {
this.clientState.undo((MqttPublish) message);
}
throw e;
}
}
複製程式碼
comms.sendNoWait方法中又呼叫了本類中的internalSend方法,並且在internalSend方法中又呼叫了clientState.send(message, token)方法繼續釋出。ClientState物件是在ClientComms初始化的構造方法中初始化的。此處需要注意一下catch裡的程式碼,下面會具體說明。
// ClientState類:
public void send(MqttWireMessage message, MqttToken token) throws MqttException {
final String methodName = "send";
...
if (message instanceof MqttPublish) {
synchronized (queueLock) {
/**
* 注意這裡:actualInFlight實際飛行中>maxInflight最大飛行中
* maxInflight:是我們在自己程式碼中通過連線選項MqttConnectOptions.setMaxInflight();設定的,預設大小為10
*/
if (actualInFlight >= this.maxInflight) {
//@TRACE 613= sending {0} msgs at max inflight window
log.fine(CLASS_NAME, methodName, "613",
new Object[]{new Integer(actualInFlight)});
throw new MqttException(MqttException.REASON_CODE_MAX_INFLIGHT);
}
MqttMessage innerMessage = ((MqttPublish) message).getMessage();
//@TRACE 628=pending publish key={0} qos={1} message={2}
log.fine(CLASS_NAME, methodName, "628",
new Object[]{new Integer(message.getMessageId()),
new Integer(innerMessage.getQos()), message});
/**
* 根據自己設定的qos等級,來決定是否需要恢復訊息
* 這裡需要說明一下qos等級區別:
* qos==0,至多傳送一次,不進行重試,Broker不會返回確認訊息。
* qos==1,至少傳送一次,確保訊息到達Broker,Broker需要返回確認訊息PUBACK
* qos==2,Broker肯定會收到訊息,且只收到一次,qos==1可能會傳送重複訊息
*/
switch (innerMessage.getQos()) {
case 2:
outboundQoS2.put(new Integer(message.getMessageId()), message);
persistence.put(getSendPersistenceKey(message), (MqttPublish) message);
break;
case 1:
outboundQoS1.put(new Integer(message.getMessageId()), message);
persistence.put(getSendPersistenceKey(message), (MqttPublish) message);
break;
}
tokenStore.saveToken(token, message);
pendingMessages.addElement(message);
queueLock.notifyAll();
}
} else {
...
}
}
複製程式碼
這段程式碼中我們發現了一個可能需要我們自己設定的屬性maxInflight,如果實際傳送中的訊息大於maxInflight約束的最大的話就會丟擲MqttException異常,那麼這個異常catch裡是怎麼處理的呢,這就要往回看一步程式碼啦,上面已經提示過需要注意ClientComms類中internalSend方法中的catch裡的程式碼:
if (message instanceof MqttPublish) {
this.clientState.undo((MqttPublish) message);
}
複製程式碼
可以很明確的看出若訊息型別是MqttPublish,則執行clientState.undo((MqttPublish) message)方法,我們前面說過訊息已經在MqttAsyncClient類的publish方法中把topic和message封裝成了MqttPublish型別的訊息,因此此處會執行undo方法:
// ClientState類:
protected void undo(MqttPublish message) throws MqttPersistenceException {
final String methodName = "undo";
synchronized (queueLock) {
//@TRACE 618=key={0} QoS={1}
log.fine(CLASS_NAME, methodName, "618",
new Object[]{new Integer(message.getMessageId()),
new Integer(message.getMessage().getQos())});
if (message.getMessage().getQos() == 1) {
outboundQoS1.remove(new Integer(message.getMessageId()));
} else {
outboundQoS2.remove(new Integer(message.getMessageId()));
}
pendingMessages.removeElement(message);
persistence.remove(getSendPersistenceKey(message));
tokenStore.removeToken(message);
if (message.getMessage().getQos() > 0) {
//Free this message Id so it can be used again
releaseMessageId(message.getMessageId());
message.setMessageId(0);
}
checkQuiesceLock();
}
}
複製程式碼
程式碼已經很明顯了,就是把大於maxInflight這部分訊息remove移除掉,因此在實際操作中要注意自己的Mqtt訊息的釋出會不會在短時間內達到maxInflight預設的10的峰值,若能達到,則需要手動設定一個適合自己專案的範圍閥值啦。
我們繼續說clientState.send(message, token)方法裡的邏輯,程式碼中註釋中也說明了Mqtt會根據qos等級來決定訊息到達機制
qos等級
- qos==0,至多傳送一次,不進行重試,Broker不會返回確認訊息,訊息可能會丟失。
- qos==1,至少傳送一次,確保訊息到達Broker,Broker需要返回確認訊息PUBACK,可能會傳送重複訊息
- qos==2,Broker肯定會收到訊息,且只收到一次
根據qos等級,若qos等於1和2,則講訊息分別加入Hashtable型別的outboundQoS1和outboundQoS2中,已在後續邏輯中確保訊息傳送成功併到達。
注:qos等級優先順序沒有maxInflight高,從程式碼中可以看出,會先判斷maxInflight再區分qos等級
程式碼的最後講訊息新增進Vector型別的pendingMessages裡,在上一篇中我們可以瞭解到MQTT的發射器是輪詢檢查pendingMessages裡是否存在資料,若存在則通過socket的OutputStream傳送出去。並且會通過接收器接收從Broker傳送回來的資料。
監聽Broker返回的訊息之資料
傳送我們就不看原始碼啦,接收我們再看一下原始碼,通過原始碼看一看資料是怎麼回到我們自己的回撥裡的:
// CommsReceiver類中:
public void run() {
recThread = Thread.currentThread();
recThread.setName(threadName);
final String methodName = "run";
MqttToken token = null;
try {
runningSemaphore.acquire();
} catch (InterruptedException e) {
running = false;
return;
}
while (running && (in != null)) {
try {
//@TRACE 852=network read message
log.fine(CLASS_NAME, methodName, "852");
receiving = in.available() > 0;
MqttWireMessage message = in.readMqttWireMessage();
receiving = false;
// 訊息是否屬於Mqtt確認型別
if (message instanceof MqttAck) {
token = tokenStore.getToken(message);
// token一般不會為空,前面已經儲存過
if (token != null) {
synchronized (token) {
// ...
clientState.notifyReceivedAck((MqttAck) message);
}
}
...
} finally {
receiving = false;
runningSemaphore.release();
}
}
}
複製程式碼
從程式碼中可以看出,Broker返回來的資料交給了clientState.notifyReceivedAck方法:
// ClientState類:
protected void notifyReceivedAck(MqttAck ack) throws MqttException {
final String methodName = "notifyReceivedAck";
...
MqttToken token = tokenStore.getToken(ack);
MqttException mex = null;
if (token == null) {
...
} else if (ack instanceof MqttPubRec) {
// qos==2 是返回
MqttPubRel rel = new MqttPubRel((MqttPubRec) ack);
this.send(rel, token);
} else if (ack instanceof MqttPubAck || ack instanceof MqttPubComp) {
// qos==1/2 訊息移除前通知的結果
notifyResult(ack, token, mex);
// Do not remove publish / delivery token at this stage
// do this when the persistence is removed later
} else if (ack instanceof MqttPingResp) {
// 連線心跳資料訊息
...
} else if (ack instanceof MqttConnack) {
// MQTT連線訊息
...
} else {
notifyResult(ack, token, mex);
releaseMessageId(ack.getMessageId());
tokenStore.removeToken(ack);
}
checkQuiesceLock();
}
複製程式碼
從上面註釋可知,釋出的訊息qos==0,返回結果是直接走else,而qos==1/2,確認訊息也最終會走到notifyResult(ack, token, mex)方法中:
protected void notifyResult(MqttWireMessage ack, MqttToken token, MqttException ex) {
final String methodName = "notifyResult";
// 取消阻止等待令牌的任何執行緒,並儲存ack
token.internalTok.markComplete(ack, ex);
// 通知此令牌已收到響應訊息,設定已完成狀態,並通過isComplete()獲取狀態
token.internalTok.notifyComplete();
// 讓使用者知道非同步操作已完成,然後刪除令牌
if (ack != null && ack instanceof MqttAck && !(ack instanceof MqttPubRec)) {
//@TRACE 648=key{0}, msg={1}, excep={2}
log.fine(CLASS_NAME, methodName, "648", new Object[]{token.internalTok.getKey(), ack,ex});
// CommsCallback類
callback.asyncOperationComplete(token);
}
// 有些情況下,由於操作失敗,因此沒有確認
if (ack == null) {
//@TRACE 649=key={0},excep={1}
log.fine(CLASS_NAME, methodName, "649", new Object[]{token.internalTok.getKey(), ex});
callback.asyncOperationComplete(token);
}
}
// Token類:
protected void markComplete(MqttWireMessage msg, MqttException ex) {
final String methodName = "markComplete";
//@TRACE 404=>key={0} response={1} excep={2}
log.fine(CLASS_NAME, methodName, "404", new Object[]{getKey(), msg, ex});
synchronized (responseLock) {
// ACK means that everything was OK, so mark the message for garbage collection.
if (msg instanceof MqttAck) {
this.message = null;
}
this.pendingComplete = true;
// 將訊息儲存在response成員變數中,並通過getWireMessage()方法獲取訊息msg
this.response = msg;
this.exception = ex;
}
}
// Token類:
protected void notifyComplete() {
...
synchronized (responseLock) {
...
if (exception == null && pendingComplete) {
// 設定已完成,並通過isComplete()獲取狀態
completed = true;
pendingComplete = false;
} else {
pendingComplete = false;
}
responseLock.notifyAll();
}
...
}
複製程式碼
此時已將MqttWireMessage訊息儲存到token中,非同步操作已完成,呼叫回撥監聽CommsCallback裡的asyncOperationComplete方法:
// CommsCallback類:
public void asyncOperationComplete(MqttToken token) {
final String methodName = "asyncOperationComplete";
if (running) {
// invoke callbacks on callback thread
completeQueue.addElement(token);
synchronized (workAvailable) {
// @TRACE 715=new workAvailable. key={0}
log.fine(CLASS_NAME, methodName, "715", new Object[]{token.internalTok.getKey()});
workAvailable.notifyAll();
}
} else {
// invoke async callback on invokers thread
try {
handleActionComplete(token);
} catch (Throwable ex) {
// Users code could throw an Error or Exception e.g. in the case
// of class NoClassDefFoundError
// @TRACE 719=callback threw ex:
log.fine(CLASS_NAME, methodName, "719", null, ex);
// Shutdown likely already in progress but no harm to confirm
clientComms.shutdownConnection(null, new MqttException(ex));
}
}
}
複製程式碼
CommsCallback是Mqtt連線就已經開始一直執行,因此running為true,所以現在已經將token新增進了completeQueue完成佇列中,CommsCallback跟發射器一樣,一直輪詢等待資料,因此此時completeQueue已有資料,此時CommsCallback的run函式則會有接下來的操作:
// CommsCallback類:
public void run() {
...
while (running) {
try {
...
if (running) {
// Check for deliveryComplete callbacks...
MqttToken token = null;
synchronized (completeQueue) {
// completeQueue不為空
if (!completeQueue.isEmpty()) {
// 獲取第一個token
token = (MqttToken) completeQueue.elementAt(0);
completeQueue.removeElementAt(0);
}
}
if (null != token) {
// token不為null,執行handleActionComplete
handleActionComplete(token);
}
...
}
if (quiescing) {
clientState.checkQuiesceLock();
}
} catch (Throwable ex) {
...
} finally {
...
}
}
}
private void handleActionComplete(MqttToken token)
throws MqttException {
final String methodName = "handleActionComplete";
synchronized (token) {
// 由上面已經,isComplete()已設定為true
if (token.isComplete()) {
// Finish by doing any post processing such as delete
// from persistent store but only do so if the action
// is complete
clientState.notifyComplete(token);
}
// 取消阻止任何服務員,如果待完成,現在設定完成
token.internalTok.notifyComplete();
if (!token.internalTok.isNotified()) {
...
// 現在呼叫非同步操作完成回撥
fireActionEvent(token);
}
...
}
}
複製程式碼
run中呼叫了handleActionComplete函式,接著後呼叫了clientState.notifyComplete()方法和fireActionEvent(token)方法,先看notifyComplete():
// ClientState類:
protected void notifyComplete(MqttToken token) throws MqttException {
final String methodName = "notifyComplete";
// 獲取儲存到Token中的Broker返回的訊息,上面有說明
MqttWireMessage message = token.internalTok.getWireMessage();
if (message != null && message instanceof MqttAck) {
...
MqttAck ack = (MqttAck) message;
if (ack instanceof MqttPubAck) {
// qos==1,使用者通知現在從永續性中刪除
persistence.remove(getSendPersistenceKey(message));
persistence.remove(getSendBufferedPersistenceKey(message));
outboundQoS1.remove(new Integer(ack.getMessageId()));
decrementInFlight();
releaseMessageId(message.getMessageId());
tokenStore.removeToken(message);
// @TRACE 650=removed Qos 1 publish. key={0}
log.fine(CLASS_NAME, methodName, "650",
new Object[]{new Integer(ack.getMessageId())});
} else if (ack instanceof MqttPubComp) {
...
}
checkQuiesceLock();
}
}
複製程式碼
再來看fireActionEvent(token)方法:
// CommsCallback類:
public void fireActionEvent(MqttToken token) {
final String methodName = "fireActionEvent";
if (token != null) {
IMqttActionListener asyncCB = token.getActionCallback();
if (asyncCB != null) {
if (token.getException() == null) {
...
asyncCB.onSuccess(token);
} else {
...
asyncCB.onFailure(token, token.getException());
}
}
}
}
複製程式碼
從這段程式碼中終於能看到回撥onSuccess和onFailure的方法啦,那asyncCB是誰呢?
// MqttToken類:
public IMqttActionListener getActionCallback() {
return internalTok.getActionCallback();
}
// Token類:
public IMqttActionListener getActionCallback() {
return callback;
}
複製程式碼
看到這,一臉懵逼,這到底是誰呢,其實我們可以直接看這個回撥設定方法,看看是從哪設定進來的就可以啦:
// Token類:
public void setActionCallback(IMqttActionListener listener) {
this.callback = listener;
}
// MqttToken類:
public void setActionCallback(IMqttActionListener listener) {
internalTok.setActionCallback(listener);
}
// ConnectActionListener類:
public void connect() throws MqttPersistenceException {
// 初始化MqttToken
MqttToken token = new MqttToken(client.getClientId());
// 將此類設定成回撥類
token.setActionCallback(this);
token.setUserContext(this);
...
}
複製程式碼
其實早在MQTT連線時,就已經將此callback設定好,因此asyncCB就是ConnectActionListener,所以此時就已經走到了ConnectActionListener類裡的onSuccess和onFailure的方法中,我們只挑一個onSuccess看一看:
// ConnectActionListener類:
public void onSuccess(IMqttToken token) {
if (originalMqttVersion == MqttConnectOptions.MQTT_VERSION_DEFAULT) {
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_DEFAULT);
}
// 此時將Broker的資料儲存進了userToken裡
userToken.internalTok.markComplete(token.getResponse(), null);
userToken.internalTok.notifyComplete();
userToken.internalTok.setClient(this.client);
comms.notifyConnect();
if (userCallback != null) {
userToken.setUserContext(userContext);
userCallback.onSuccess(userToken);
}
if (mqttCallbackExtended != null) {
String serverURI =
comms.getNetworkModules()[comms.getNetworkModuleIndex()].getServerURI();
mqttCallbackExtended.connectComplete(reconnect, serverURI);
}
}
複製程式碼
這裡的userCallback又是誰呢?上一篇其實說過的,userCallback其實就是MqttConnection.connect函式中IMqttActionListener listener,所以此時又來到了MqttConnection類裡connect方法裡的listener監聽回撥內:
// MqttConnection類:
public void connect(MqttConnectOptions options, String invocationContext,
String activityToken) {
...
service.traceDebug(TAG, "Connecting {" + serverURI + "} as {" + clientId + "}");
final Bundle resultBundle = new Bundle();
resultBundle.putString(MqttServiceConstants.CALLBACK_ACTIVITY_TOKEN,
activityToken);
resultBundle.putString(
MqttServiceConstants.CALLBACK_INVOCATION_CONTEXT,
invocationContext);
resultBundle.putString(MqttServiceConstants.CALLBACK_ACTION,
MqttServiceConstants.CONNECT_ACTION);
try {
...
// 此時邏輯已經來到這裡
IMqttActionListener listener = new MqttConnectionListener(
resultBundle) {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
// 執行如下程式碼:
doAfterConnectSuccess(resultBundle);
service.traceDebug(TAG, "connect success!");
}
@Override
public void onFailure(IMqttToken asyncActionToken,
Throwable exception) {
resultBundle.putString(
MqttServiceConstants.CALLBACK_ERROR_MESSAGE,
exception.getLocalizedMessage());
resultBundle.putSerializable(
MqttServiceConstants.CALLBACK_EXCEPTION, exception);
service.traceError(TAG,
"connect fail, call connect to reconnect.reason:"
+ exception.getMessage());
doAfterConnectFail(resultBundle);
}
};
if (myClient != null) {
if (isConnecting) {
...
} else {
service.traceDebug(TAG, "myClient != null and the client is not connected");
service.traceDebug(TAG, "Do Real connect!");
setConnectingState(true);
myClient.connect(connectOptions, invocationContext, listener);
}
}
// if myClient is null, then create a new connection
else {
...
myClient.connect(connectOptions, invocationContext, listener);
}
} catch (Exception e) {
...
}
}
複製程式碼
由這段程式碼以及註釋可以知道,現在以及執行到了MqttConnection類裡的doAfterConnectSuccess方法裡:
// MqttConnection類:
private void doAfterConnectSuccess(final Bundle resultBundle) {
// 獲取喚醒鎖
acquireWakeLock();
service.callbackToActivity(clientHandle, Status.OK, resultBundle);
deliverBacklog();
setConnectingState(false);
disconnected = false;
// 釋放喚醒鎖
releaseWakeLock();
}
private void deliverBacklog() {
Iterator<StoredMessage> backlog = service.messageStore
.getAllArrivedMessages(clientHandle);
while (backlog.hasNext()) {
StoredMessage msgArrived = backlog.next();
Bundle resultBundle = messageToBundle(msgArrived.getMessageId(),
msgArrived.getTopic(), msgArrived.getMessage());
// 關注下這個action,下面會用到
resultBundle.putString(MqttServiceConstants.CALLBACK_ACTION,
MqttServiceConstants.MESSAGE_ARRIVED_ACTION);
service.callbackToActivity(clientHandle, Status.OK, resultBundle);
}
}
複製程式碼
可以看到這個函式中呼叫了幾個方法中的其中兩個service.callbackToActivity(clientHandle, Status.OK, resultBundle);和deliverBacklog();,deliverBacklog()方法最後也是呼叫的service.callbackToActivity方法。所以直接看service.callbackToActivity:
// MqttService類:
void callbackToActivity(String clientHandle, Status status,
Bundle dataBundle) {
// 傳送廣播
Intent callbackIntent = new Intent(
MqttServiceConstants.CALLBACK_TO_ACTIVITY);
if (clientHandle != null) {
callbackIntent.putExtra(
MqttServiceConstants.CALLBACK_CLIENT_HANDLE, clientHandle);
}
callbackIntent.putExtra(MqttServiceConstants.CALLBACK_STATUS, status);
if (dataBundle != null) {
callbackIntent.putExtras(dataBundle);
}
LocalBroadcastManager.getInstance(this).sendBroadcast(callbackIntent);
}
複製程式碼
service.callbackToActivity方法其實就是傳送廣播,那誰來接收廣播呢?其實接收廣播的就在最開始的MqttAndroidClient,MqttAndroidClient繼承自BroadcastReceiver,所以說MqttAndroidClient本身就是一個廣播接收者,所以我們來看它的onReceive方法:
// MqttAndroidClient類:
@Override
public void onReceive(Context context, Intent intent) {
Bundle data = intent.getExtras();
String handleFromIntent = data
.getString(MqttServiceConstants.CALLBACK_CLIENT_HANDLE);
if ((handleFromIntent == null)
|| (!handleFromIntent.equals(clientHandle))) {
return;
}
String action = data.getString(MqttServiceConstants.CALLBACK_ACTION);
// 判斷訊息的action型別
if (MqttServiceConstants.CONNECT_ACTION.equals(action)) {
connectAction(data);
} else if (MqttServiceConstants.CONNECT_EXTENDED_ACTION.equals(action)) {
connectExtendedAction(data);
} else if (MqttServiceConstants.MESSAGE_ARRIVED_ACTION.equals(action)) {
messageArrivedAction(data);
} else if (MqttServiceConstants.SUBSCRIBE_ACTION.equals(action)) {
subscribeAction(data);
} else if (MqttServiceConstants.UNSUBSCRIBE_ACTION.equals(action)) {
unSubscribeAction(data);
} else if (MqttServiceConstants.SEND_ACTION.equals(action)) {
// 釋出成功與否的回撥
sendAction(data);
} else if (MqttServiceConstants.MESSAGE_DELIVERED_ACTION.equals(action)) {
messageDeliveredAction(data);
} else if (MqttServiceConstants.ON_CONNECTION_LOST_ACTION
.equals(action)) {
connectionLostAction(data);
} else if (MqttServiceConstants.DISCONNECT_ACTION.equals(action)) {
disconnected(data);
} else if (MqttServiceConstants.TRACE_ACTION.equals(action)) {
traceAction(data);
} else {
mqttService.traceError(MqttService.TAG, "Callback action doesn't exist.");
}
}
複製程式碼
從程式碼和註釋以及上面的deliverBacklog方法中可以知道,我們現在需要關注的action為MESSAGE_ARRIVED_ACTION,所以就可以呼叫方法messageArrivedAction(data):
// MqttAndroidClient類:
private void messageArrivedAction(Bundle data) {
if (callback != null) {
String messageId = data
.getString(MqttServiceConstants.CALLBACK_MESSAGE_ID);
String destinationName = data
.getString(MqttServiceConstants.CALLBACK_DESTINATION_NAME);
ParcelableMqttMessage message = data
.getParcelable(MqttServiceConstants.CALLBACK_MESSAGE_PARCEL);
try {
if (messageAck == Ack.AUTO_ACK) {
callback.messageArrived(destinationName, message);
mqttService.acknowledgeMessageArrival(clientHandle, messageId);
} else {
message.messageId = messageId;
callback.messageArrived(destinationName, message);
}
// let the service discard the saved message details
} catch (Exception e) {
// Swallow the exception
}
}
}
@Override
public void setCallback(MqttCallback callback) {
this.callback = callback;
}
複製程式碼
在messageArrivedAction方法中可以看到,我們最後呼叫了callback回撥了messageArrived方法,那麼 callback通過上面下部分程式碼可以知道,其實這個callback就是我們上一篇文章中所說的我們初始化MqttAndroidClient後,通過方法setCallback設定的我們自己定義的實現MqttCallback介面的回撥類。
監聽Broker返回的訊息之釋出訊息成功與否
再看下sendAction(data)方法:
private void sendAction(Bundle data) {
IMqttToken token = getMqttToken(data);
// remove on delivery
simpleAction(token, data);
}
private void simpleAction(IMqttToken token, Bundle data) {
if (token != null) {
Status status = (Status) data
.getSerializable(MqttServiceConstants.CALLBACK_STATUS);
if (status == Status.OK) {
// 如果釋出成功回撥此方法
((MqttTokenAndroid) token).notifyComplete();
} else {
Exception exceptionThrown =
(Exception) data.getSerializable(MqttServiceConstants.CALLBACK_EXCEPTION);
// 釋出失敗回撥
((MqttTokenAndroid) token).notifyFailure(exceptionThrown);
}
} else {
if (mqttService != null) {
mqttService.traceError(MqttService.TAG, "simpleAction : token is null");
}
}
}
複製程式碼
接下來再看一看釋出成功回撥的MqttTokenAndroid的notifyComplete函式:
// MqttTokenAndroid類:
void notifyComplete() {
synchronized (waitObject) {
isComplete = true;
waitObject.notifyAll();
if (listener != null) {
listener.onSuccess(this);
}
}
}
複製程式碼
這裡又呼叫了listener.onSuccess(this)方法,那麼這個listener是誰?其實listener就是我們呼叫MqttAndroidClient類的publish釋出的最後一個引數,即我們自定義的監聽釋出訊息是否釋出成功的回撥類。上面在MqttConnection類的publish方法中封裝過MqttServiceConstants.SEND_ACTION的Bundle資料,而此資料是被MqttConnection類裡的MqttConnectionListener攜帶。所以MqttConnectionListener裡的onSuccess被呼叫時就會呼叫service.callbackToActivity,繼而到sendBroadcast傳送廣播,最後呼叫sendAction方法,回撥自定義的IMqttActionListener的實現類。而MqttConnectionListener裡的onSuccess是在CommsCallback類裡的fireActionEvent方法中,往上走就到CommsCallback類的了handleActionComplete和run()函式。
現在看是不是有點懵畢竟上面有兩個 監聽Broker返回的訊息,一個是用來監聽Broker發給客戶端資料的監聽,另一個是客戶端釋出訊息是否釋出成功的監聽而已。兩者都是使用MqttActionListener,不過前者在MqttActionListener監聽回撥裡最後呼叫的是自定義的MqttCallback回撥而已。並且兩者監聽的位置不一樣,前者是在 MqttConnection類的connect時就已確認下來的,對於一個MQTT連線只會有一個,所以這個是一直用來監聽資料的;而後者監聽釋出訊息是否成功是每個publish都需要傳入的,並在MqttConnection類裡的publish初始化。這麼講是不是就清晰一些啦。
哈哈,到此MQTT的publish釋出以及接收Broker資料的原始碼分析也看完啦。
(注:若有什麼地方闡述有誤,敬請指正。)