1. 程式人生 > >Android APP必備高階功能,訊息推送之MQTT

Android APP必備高階功能,訊息推送之MQTT

http://www.itnose.net/detail/6652162.html

1. Android端實現訊息推送的幾種方式

  1. 輪詢:客戶端定時向伺服器請求資料。偽推送。缺點:費電,費流量。
  2. 攔截簡訊訊息。伺服器需要向客戶端發通知時,傳送一條簡訊,客戶端收到特定簡訊之後,先獲取資訊,然後攔截簡訊。偽推送。缺點:貴而且簡訊可能被安全軟體攔截。
  3. 持久連線(Push)方式:客戶端和伺服器之間建立長久連線。真正的推送。
    1. Google的C2DM(Cloudto Device Messaging)。需要科學上網,國內大多數使用者無法使用。
    2. XMPP。XMPP(可擴充套件通訊和表示協議)是基於可擴充套件標記語言(XML)的協議。androidpn是一個基於XMPP協議的java開源Android push notification實現。它包含了完整的客戶端和伺服器端。
    3. MQTT。MQTT是一個輕量級的訊息釋出/訂閱協議,它是實現基於手機客戶端的訊息推送伺服器的理想解決方案。

2. MQTT簡介

建議時間充裕的同學有順序的閱讀上文五個連結內容,不充裕的同學請看下面簡單的介紹(內容大多來自上面五條連結)。

MQTT 協議
客戶機較小並且 MQTT 協議 高效地使用網路頻寬,在這個意義上,其為輕量級。MQTT 協議支援可靠的傳送和即發即棄的傳輸。 在此協議中,訊息傳送與應用程式脫離。 脫離應用程式的程度取決於寫入 MQTT 客戶機和 MQTT 伺服器的方式。脫離式傳送能夠將應用程式從任何伺服器連線和等待訊息中解脫出來。 互動模式與電子郵件相似,但在應用程式程式設計方面進行了優化。

協議具有許多不同的功能:

它是一種釋出/預訂協議。除提供一對多訊息分發外,釋出/預訂也脫離了應用程式。對於具有多個客戶機的應用程式來說,這些功能非常有用。它與訊息內容沒有任何關係。它通過 TCP/IP 執行,TCP/IP 可以提供基本網路連線。它針對訊息傳送提供三種服務質量:
“至多一次”
訊息根據底層因特網協議網路盡最大努力進行傳遞。 可能會丟失訊息。
例如,將此服務質量與通訊環境感測器資料一起使用。 對於是否丟失個別讀取或是否稍後立即釋出新的讀取並不重要。“至少一次”
保證訊息抵達,但可能會出現重複。“剛好一次”
確保只收到一次訊息。
例如,將此服務質量與記帳系統一起使用。 重複或丟失訊息可能會導致不便或收取錯誤費用。它是一種管理網路中訊息流的經濟方式。 例如,固定長度的標題僅 2 個位元組長度,並且協議交換可最大程度地減少網路流量。它具有一種“遺囑”功能,該功能通知訂戶客戶機從 MQTT 伺服器異常斷開連線。請參閱“
最後的訊息
”釋出。

3. MQTT伺服器搭建

  1. 點選這裡,下載Apollo伺服器,解壓後安裝。
  2. 命令列進入安裝目錄bin目錄下(例:E:>cd E:\MQTT\apache-apollo-1.7.1\bin)。
  3. 輸入apollo create XXX(xxx為建立的伺服器例項名稱,例:apollo create mybroker),之後會在bin目錄下建立名稱為XXX的資料夾。XXX資料夾下etc\apollo.xml檔案下是配置伺服器資訊的檔案。etc\users.properties檔案包含連線MQTT伺服器時用到的使用者名稱和密碼,預設為admin=password,即賬號為admin,密碼為password,可自行更改。
  4. 進入XXX/bin目錄,輸入apollo-broker.cmd run開啟伺服器,看到如下介面代表搭建完成

success

4. MQTT Android客戶端具體實現

基本概念:

topic:中文意思是“話題”。在MQTT中訂閱了(subscribe)同一話題(topic)的客戶端會同時收到訊息推送。直接實現了“群聊”功能。clientId:客戶身份唯一標識。qos:服務質量。retained:要保留最後的斷開連線資訊。MqttAndroidClient#subscribe():訂閱某個話題。MqttAndroidClient#publish(): 向某個話題傳送訊息,之後伺服器會推送給所有訂閱了此話題的客戶。userName:連線到MQTT伺服器的使用者名稱。passWord :連線到MQTT伺服器的密碼。

新增依賴

repositories {
    maven {
        url "https://repo.eclipse.org/content/repositories/paho-releases/"
    }
}

dependencies {
    compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
    compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.0'
}

新增限權

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

註冊Service

        <!-- Mqtt Service -->
        <service android:name="org.eclipse.paho.android.service.MqttService" />
        <service android:name="com.dongyk.service.MQTTService"/>

Android端具體實現

package com.dongyk.service;

import android.app.Service;
...

/**
 * MQTT長連線服務
 *
 * @author 一口仨饃 聯絡方式 : [email protected]
 * @version 建立時間:2016/9/16 22:06
 */
public class MQTTService extends Service {

    public static final String TAG = MQTTService.class.getSimpleName();

    private static MqttAndroidClient client;
    private MqttConnectOptions conOpt;

// private String host = "tcp://10.0.2.2:61613";
    private String host = "tcp://192.168.1.103:61613";
    private String userName = "admin";
    private String passWord = "password";
    private static String myTopic = "topic";
    private String clientId = "test";

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        init();
        return super.onStartCommand(intent, flags, startId);
    }

    public static void publish(String msg){
        String topic = myTopic;
        Integer qos = 0;
        Boolean retained = false;
        try {
            client.publish(topic, msg.getBytes(), qos.intValue(), retained.booleanValue());
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    private void init() {
        // 伺服器地址(協議+地址+埠號)
        String uri = host;
        client = new MqttAndroidClient(this, uri, clientId);
        // 設定MQTT監聽並且接受訊息
        client.setCallback(mqttCallback);

        conOpt = new MqttConnectOptions();
        // 清除快取
        conOpt.setCleanSession(true);
        // 設定超時時間,單位:秒
        conOpt.setConnectionTimeout(10);
        // 心跳包傳送間隔,單位:秒
        conOpt.setKeepAliveInterval(20);
        // 使用者名稱
        conOpt.setUserName(userName);
        // 密碼
        conOpt.setPassword(passWord.toCharArray());

        // last will message
        boolean doConnect = true;
        String message = "{\"terminal_uid\":\"" + clientId + "\"}";
        String topic = myTopic;
        Integer qos = 0;
        Boolean retained = false;
        if ((!message.equals("")) || (!topic.equals(""))) {
            // 最後的遺囑
            try {
                conOpt.setWill(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
            } catch (Exception e) {
                Log.i(TAG, "Exception Occured", e);
                doConnect = false;
                iMqttActionListener.onFailure(null, e);
            }
        }

        if (doConnect) {
            doClientConnection();
        }

    }

    @Override
    public void onDestroy() {
        try {
            client.disconnect();
        } catch (MqttException e) {
            e.printStackTrace();
        }
        super.onDestroy();
    }

    /** 連線MQTT伺服器 */
    private void doClientConnection() {
        if (!client.isConnected() && isConnectIsNomarl()) {
            try {
                client.connect(conOpt, null, iMqttActionListener);
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }

    }

    // MQTT是否連線成功
    private IMqttActionListener iMqttActionListener = new IMqttActionListener() {

        @Override
        public void onSuccess(IMqttToken arg0) {
            Log.i(TAG, "連線成功 ");
            try {
                // 訂閱myTopic話題
                client.subscribe(myTopic,1);
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(IMqttToken arg0, Throwable arg1) {
            arg1.printStackTrace();
              // 連線失敗,重連
        }
    };

    // MQTT監聽並且接受訊息
    private MqttCallback mqttCallback = new MqttCallback() {

        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {

            String str1 = new String(message.getPayload());
            MQTTMessage msg = new MQTTMessage();
            msg.setMessage(str1);
            EventBus.getDefault().post(msg);
            String str2 = topic + ";qos:" + message.getQos() + ";retained:" + message.isRetained();
            Log.i(TAG, "messageArrived:" + str1);
            Log.i(TAG, str2);
        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken arg0) {

        }

        @Override
        public void connectionLost(Throwable arg0) {
            // 失去連線,重連
        }
    };

    /** 判斷網路是否連線 */
    private boolean isConnectIsNomarl() {
        ConnectivityManager connectivityManager = (ConnectivityManager) this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = connectivityManager.getActiveNetworkInfo();
        if (info != null && info.isAvailable()) {
            String name = info.getTypeName();
            Log.i(TAG, "MQTT當前網路名稱:" + name);
            return true;
        } else {
            Log.i(TAG, "MQTT 沒有可用網路");
            return false;
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

首先初始化各個引數,之後連線伺服器。連線成功之後在http://127.0.0.1:61680/看到自動建立了名稱為”topic”的topic。這裡我使用了一個真機和一個模擬器執行程式。http://127.0.0.1:61680/服務端看到的是這個樣子

serverPic

這裡需要注意兩個地方
1. 模擬器執行的時候host = "tcp://10.0.2.2:61613",因為10.0.2.2 是模擬器設定的特定ip,是你電腦的別名。真機執行的時候host = "tcp://192.168.1.103:61613"。192.168.1.103是我主機的IPv4地址,檢視本機IP的cmd命令為ipconfig/all。
2. 兩次執行時的clientId不能一樣(為了保證客戶標識的唯一性)。

這裡為了測試,在MQTTService中暴露了一個公共方法publish(String msg)給MainActivity呼叫。程式碼如下

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EventBus.getDefault().register(this);
        startService(new Intent(this, MQTTService.class));
        findViewById(R.id.publishBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MQTTService.publish("CSDN 一口仨饃");
            }
        });
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void getMqttMessage(MQTTMessage mqttMessage){
        Log.i(MQTTService.TAG,"get message:"+mqttMessage.getMessage());
        Toast.makeText(this,mqttMessage.getMessage(),Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onDestroy() {
        EventBus.getDefault().unregister(this);
        super.onDestroy();
    }

}

這裡使用了EventBus3.0傳送訊息,感興趣的可以看下EventBus3.0使用及原始碼解析。當然,你也可以使用介面回撥的方式甚至直接在Service中彈出Toast。whatever,現在點選一個客戶端MainActivity中的Button,兩個客戶端已經能同時彈出訊息。已經get到資料了。接下來,show time~

相關推薦

Android APP必備高階功能訊息MQTT

http://www.itnose.net/detail/6652162.html 1. Android端實現訊息推送的幾種方式 輪詢:客戶端定時向伺服器請求資料。偽推送。缺點:費電,費流量。攔截簡訊訊息。伺服器需要向客戶端發通知時,傳送一條簡訊,客戶端收到特定簡訊之後

為什麼安卓手機上的多數APP訊息不了?

有人問: 蘋果手機上的APP,只要設定好了,訊息推送就沒有一點問題; 而安卓手機上,為什麼只有一部分APP(如微信等),能正常推送;而其它的APP,怎麼設定也沒有及時訊息推送? 個人的分析理解如下,說得不對的地方請各位同學斧正。呵呵。 蘋果有自己的訊息推送機制。安

Android開發-在Android應用裡整合友盟訊息SDK的實現(相容小米、華為、魅族機型離線

前 言 最近由於專案的功能需求的需要,需要在Android應用整合訊息推送的功能,而目前市面上的第三方訊息推送除了友盟推送外,還有極光推送、小米推送、個推以及信鴿(騰訊)推送等。當時本人對比各大第三方的訊息推送進行了測試,覺得友盟訊息推送整合簡單,推送訊息的

Android裡的Xmpp的理解(訊息)

 XMPP(可擴充套件通訊和表示協議)是基於可擴充套件標記語言(XML)的協議,它用於即時訊息(IM)以及線上探測。這個協議可能最終允許因特網使用者向因特網上的其他任何人傳送即時訊息。用xmpp來實現android的push功能,感覺有點大材小用了,xmpp本身是一種即時

SignalR快速入門 ~ 仿QQ即時聊天訊息單聊群聊多群公聊(基礎=》提升)

 SignalR快速入門 ~ 仿QQ即時聊天,訊息推送,單聊,群聊,多群公聊(基礎=》提升,5個Demo貫徹全篇,感興趣的玩才是真的學) 應用情景之一:   沒太多連續的時間來研究SignalR,所以我把這篇文章分了三個階段: 第一個階段,簡單使用,熟悉並認識SignalR 第二個階段,實現

nodejs訊息socket.io 與 ws對比

node.js的websocket庫目前比較熱門的是ws和socket.io。我們對比一下這兩個庫。 一、筆者寫這篇文章時,ws的周下載量是4百多萬,最近一次更新是11天前,總共98個版本。 socket.io周下載量接近2百萬,最近一次更新是三個月前。總共110個版本 從n

nodejs訊息ws

nodejs伺服器端,如何訊息的型別為test就返回訊息,反之則群發: const WebSocket = require('ws'); const wss = new WebSocket.Server({port:3030}) const connection = {} wss.on('co

【小程式】如何實現訊息收集

當訂單狀態變更時,小程式如何實現訊息推送來通知到使用者呢。微信開放了一個叫模板訊息的功能。  要實現訊息推送,分三步走 一、前期配置工作 二、前端工作 要實現推送訊息給使用者,就要有推送碼,官方API介紹提交一次表單有一次推送機會,完成一次支付行為有三次推送機

(二)websocket實現訊息基於spring4.0實現

  1、新建springBoot專案,新增依賴        &n

android 實現mqtt訊息以及不停斷線重連的問題解決

前段時間專案用到mqtt的訊息推送,整理一下程式碼,程式碼的原型是網上找的,具體哪個地址已經忘記了。 程式碼的實現是新建了一個MyMqttService,全部功能都在裡面實現,包括連伺服器,斷線重連,訂閱訊息,處理訊息,釋出訊息等基本操作。 首先新增依賴: dependencies { &

Android UDP - 簡單訊息功能

近期接觸了幾個小的Android開發專案,根據需求要利用到網路傳輸中的UDP協議方式傳輸,遇到不少坑,在此分享一下在Android應用中UDP的一些簡單技術功能實現,希望能幫到用得到的同僚。 需(wo)求(yao)分(gan)析(ma): 從PC上輸入一串亂七八糟,然後能在我手機(某為pow

Android App訊息 實現原理

1.訊息推送的實質實際上,是當伺服器有新訊息需推送給使用者時,先發送給應用App,應用App再發送給使用者2. 作用產品角度:功能需要,如:資訊類產品的新聞推送、工具類產品的公告推送等等運營角度:活動運營需要,如:電商類產品的促銷活動;召回使用者 / 提高活躍度等等作為開發者

iOS經典講解實現App訊息功能(二)

作者:Loving_iOS 上一篇部落格iOS經典講解之實現App訊息推送功能(一)講解了實現訊息推送的的準備工作,接下來我們來講解第二部分的內容,實現具體的推送及程式碼示例。 訊息推送的第三方平臺有很多,這裡我們使用極光推送平臺,註冊極光推送平臺的賬號。 登陸後進入控

Android訊息(二)--基於MQTT協議實現的功能

前段時間公司需要android端的手機群推功能,我們就通過MQTT來實現了該功能。 MQTT的官網如下 http://mqtt.org/ 關於系統的主要架構就不詳述了。這關係的到職業道德問題,在這裡

Android訊息接收後通知欄的顯示

訊息推送接收到後,顯示通知欄 public static void showNotifictionIcon(Context context) { NotificationCom

android客戶端訊息功能實現方案

1.使用第三方推送平臺 如友盟、極光、百度等現成的訊息推送。好處:訊息及時、穩定,整合簡單。不需要自己搭建支援伺服器,但是可能涉及到收費、保密、服務質量、擴充套件等問題。 2、MQTT協議實現android推送 MQTT是一個輕

訊息一個好功能90%的開發者都不知道

1.個推報表簡介 推送資料報表主要用於統計某一條訊息的具體下發情況。單條推送訊息下發使用者總量有多少,其中成功推送到手機的數量有多

Android 基於Netty的訊息方案物件的傳遞(四)

在上一篇文章中《Android 基於Netty的訊息推送方案之字串的接收和傳送(三)》我們介紹了Netty的字串傳遞,我們知道了Netty的訊息傳遞都是基於流,通過ChannelBuffer傳遞的,那麼自然,Object也需要轉換成ChannelBuffer來傳遞。好在Netty本身已經給我們寫好了

Android 基於Netty的訊息方案字串的接收和傳送(三)

在上一篇文章中《Android 基於Netty的訊息推送方案之概念和工作原理(二)》 ,我們介紹過一些關於Netty的概念和工作原理的內容,今天我們先來介紹一個叫做ChannelBuffer的東東。 ChannelBuffer  Netty中的訊息傳遞,都必須以位元

Android 基於Netty的訊息方案概念和工作原理(二)

上一篇文章中我講述了關於訊息推送的方案以及一個基於Netty實現的一個簡單的Hello World,為了更好的理解Hello World中的程式碼,今天我來講解一下關於Netty中一些概念和工作原理的內容,如果你覺得本篇文章有些枯燥,請先去閱讀《Android 基於Netty的訊息推送方案之Hell