個人博客:haichenyi.com。感謝關(guān)注
整體介紹
??最近公司用到的推送MQTT。不想過多的介紹背景什么的,我就直接講怎么實現(xiàn)這個功能早像。
??他這個原理長連接,這個不用多講肖爵,用法類似于EventBus卢鹦,需要先訂閱,然后通過topic再發(fā)送消息劝堪。topic是什么呢冀自?我先來講講整體流程:
先連接服務(wù)器揉稚,要先建立長連接
然后需要訂閱topic,連接之后才能訂閱topic
最后就是通過topic推送消息熬粗,接收消息
一步一步講:
第一步搀玖,與服務(wù)器建立連接
??先丟代碼,然后看注釋:
private void initPush() {
// 服務(wù)器地址(協(xié)議+地址+端口號)
String uri = host;
client = new MqttAndroidClient(this, uri, clientId);
// 設(shè)置MQTT監(jiān)聽并且接受消息
client.setCallback(mqttCallback);
//Mqtt的一些設(shè)置
conOpt = new MqttConnectOptions();
conOpt.setAutomaticReconnect(true);
// 清除緩存
conOpt.setCleanSession(true);
// 設(shè)置超時時間驻呐,單位:秒
conOpt.setConnectionTimeout(10);
// 心跳包發(fā)送間隔灌诅,單位:秒
conOpt.setKeepAliveInterval(20);
myTopic = String.format(TOPIC_SUB, mDeviceId);
Log.e(TAG,"myTopic_________"+myTopic);
doClientConnection();
}
??上面的這些參數(shù),我碰到了兩個問題含末。
??上面的這些參數(shù)猜拾,我碰到了兩個問題。
??上面的這些參數(shù)答渔,我碰到了兩個問題关带。
- 第一個問題侥涵,與服務(wù)器建立連接沼撕,你得先有一個服務(wù)器吧?我根據(jù)網(wǎng)上的步驟芜飘,創(chuàng)建了一個apache-apollo服務(wù)器务豺,并且啟動了,也啟動成功了嗦明,我建立連接的時候笼沥,總是失敗。然后娶牌,找啊找奔浅,找啊找。問題沒有解決诗良,但是汹桦,我找到了一個可以用的服務(wù)器,也就是這里的uri鉴裹,不要設(shè)置MqttConnectOptions的用戶名和密碼舞骆,設(shè)置了他會拒絕
private String host = "tcp://test.mosquitto.org:1883";
- 第二個問題,我連接成功之后径荔,不一會督禽,他就會自動斷開連接,或者总处,推送完消息之后狈惫,他就會斷開連接。然后鹦马,網(wǎng)上搜原因胧谈,找啊找玖院,誒,我找到了第岖。MqttAndroidClient的構(gòu)造方法:
/**
* Constructor - create an MqttAndroidClient that can be used to communicate with an MQTT server on android
*
* @param context
* object used to pass context to the callback.
* @param serverURI
* specifies the protocol, host name and port to be used to
* connect to an MQTT server
* @param clientId
* specifies the name by which this connection should be
* identified to the server
*/
public MqttAndroidClient(Context context, String serverURI,
String clientId) {
this(context, serverURI, clientId, null, Ack.AUTO_ACK);
}
看第三個參數(shù)难菌,clientId,指定一個名字蔑滓,用來連接服務(wù)器的身份標識郊酒。就是說,你設(shè)置的這個值键袱,是你在服務(wù)器的唯一標識燎窘,不能跟其他用戶的相同。我把這個clientId直接用uuid生成蹄咖,就沒問題了褐健。
第二步,訂閱topic
??回到上面澜汤,接著往下面走蚜迅,
/**
* 連接MQTT服務(wù)器
*/
private void doClientConnection() {
if (!client.isConnected() && isConnectIsNormal()) {
try {
client.connect(conOpt, null, iMqttActionListener);
} catch (MqttException e) {
e.printStackTrace();
}
}
}
/**
* 判斷網(wǎng)絡(luò)是否連接
*/
private boolean isConnectIsNormal() {
ConnectivityManager connectivityManager = (ConnectivityManager) this.getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager != null) {
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if (info != null && info.isAvailable()) {
String name = info.getTypeName();
Log.e(TAG, "MQTT當前網(wǎng)絡(luò)名稱:" + name);
return true;
} else {
Log.e(TAG, "MQTT 沒有可用網(wǎng)絡(luò)");
return false;
}
} else {
return false;
}
}
??這個方法就是用來連接服務(wù)器的,首先判斷是否正在連接俊抵,后面那個是判斷當前有沒有網(wǎng)絡(luò)谁不。再就是這個iMqttActionListener監(jiān)聽了
// MQTT是否連接成功
private IMqttActionListener iMqttActionListener = new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken arg0) {
Log.e(TAG, "連接成功 ");
try {
// 訂閱myTopic話題
client.subscribe(myTopic, 0);
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(IMqttToken arg0, Throwable arg1) {
Log.e(TAG, "連接失敗");
arg1.printStackTrace();
// 連接失敗,重連
}
};
??訥徽诲,就是這里刹帕,你如果服務(wù)器有問題,他一直走onFailure方法谎替。服務(wù)器連接成功之后偷溺,就是訂閱topic。我來說說這個
client.subscribe(myTopic, 0);
??首先钱贯,這個主題挫掏,是你自己跟服務(wù)器商量好的,隨便什么都可以喷舀。為什么要訂閱主題呢砍濒?我提前給你瞅瞅推送消息是怎么推送的
??第二個參數(shù),消息的類型qos硫麻,有三種:0爸邢、1、2
- 0代表“至多一次”拿愧,消息發(fā)布完全依賴底層 TCP/IP 協(xié)議杠河。會發(fā)生消息丟失或重復。這一級別可用于如下情況,環(huán)境傳感器數(shù)據(jù)券敌,丟失一次讀記錄無所謂唾戚,因為不久后還會有第二次發(fā)送
- 1代表“至少一次”,確保消息到達待诅,但消息重復可能會發(fā)生
- 2代表“只有一次”叹坦,確保消息到達一次。這一級別可用于如下情況卑雁,在計費系統(tǒng)中募书,消息重復或丟失會導致不正確的結(jié)果。
??簡單說明下测蹲,如果發(fā)送的是臨時的消息莹捡,例如給某topic所有在線的設(shè)備發(fā)送一條消息,丟失的話也無所謂扣甲,0就可以了篮赢。如果需要客戶端保證能接收消息,需要指定QoS為1
client.publish(topic, new MqttMessage(msg.getBytes()));
??訥琉挖,推送消息启泣,是根據(jù)topic推送的,第二個參數(shù)粹排,就是你要推送的具體消息种远。我個人認為涩澡,你可以理解成就類似于鍵值對的形式顽耳,
不同的用戶可以訂閱相同的主題
不同的用戶可以訂閱相同的主題
不同的用戶可以訂閱相同的主題
??這個就是跟其他長連接不同的地方,底層妙同,其實都一樣射富,雖然我沒有看底層的代碼。想也想的到粥帚,服務(wù)器肯定是根據(jù)這個主題胰耗,去找對應(yīng)的用戶,然后推送消息芒涡。而其他的長連接就是直接指定用戶柴灯。跑題了,跑題了费尽。
第三步赠群,推送、接收消息
??當你連接服務(wù)器成功之后旱幼,就要推送消息了查描,我用的EventBus發(fā)的
private void publishData(String msg) {
String topic = myTopic;
try {
Log.e(TAG,"給__"+topic+"__topic發(fā)送的消息為:"+msg);
client.publish(topic, new MqttMessage(msg.getBytes()));
} catch (MqttException e) {
e.printStackTrace();
}
}
// MQTT監(jiān)聽并且接受消息
private MqttCallback mqttCallback = new MqttCallback() {
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
Log.e(TAG,"接受到__"+topic+"__topic的消息為:"+new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken arg0) {
Log.e(TAG,"deliveryComplete");
}
@Override
public void connectionLost(Throwable arg0) {
// 失去連接,重連
Log.e(TAG,"失去連接");
}
};
??當你的clientId重復的時候,他就會一直走connectionLost方法冬三。到這里匀油,基本上就講完了,要注意的是勾笆,退出的時候敌蚜,記得要釋放資源
@Override
public void onDestroy() {
try {
if (client != null && client.isConnected()) {
client.disconnect();
}
} catch (MqttException e) {
e.printStackTrace();
}
EventBus.getDefault().unregister(this);
super.onDestroy();
}
網(wǎng)上很多都是直接講整體流程,重來不講中間碰到的問題窝爪。難受