Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android端實現消息推送

Android端實現消息推送

編輯:關於Android編程

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

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

2. MQTT簡介

MQTT官網:http://mqtt.org/

MQTT介紹:http://www.ibm.com

MQTT Android github:https://github.com/eclipse/paho.mqtt.android

MQTT API:http://www.eclipse.org/paho/files/javadoc/index.html

MQTT Android API: http://www.eclipse.org/paho/files/android-javadoc/index.html

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

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

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

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

3. MQTT服務器搭建

點擊這裡,下載Apollo服務器,解壓後安裝。 命令行進入安裝目錄bin目錄下(例:E:>cd E:\MQTT\apache-apollo-1.7.1\bin)。 輸入apollo create XXX(xxx為創建的服務器實例名稱,例:apollo create mybroker),之後會在bin目錄下創建名稱為XXX的文件夾。XXX文件夾下etc\apollo.xml文件下是配置服務器信息的文件。etc\users.properties文件包含連接MQTT服務器時用到的用戶名和密碼,默認為admin=password,即賬號為admin,密碼為password,可自行更改。 進入XXX/bin目錄,輸入apollo-broker.cmd run開啟服務器,看到如下界面代表搭建完成

之後在浏覽器輸入http://127.0.0.1:61680/,查看是否安裝成功。

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'
}

添加限權

    
    
    

注冊Service

        
        
        

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/服務端看到的是這個樣子

這裡需要注意兩個地方
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發送消息。當然,你也可以使用接口回調的方式甚至直接在Service中彈出Toast。whatever,現在點擊一個客戶端MainActivity中的Button,兩個客戶端已經能同時彈出消息。已經get到數據了。接下來,show time~

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved