我是靠谱客的博主 不安烤鸡,最近开发中收集的这篇文章主要介绍Android 二维码被扫后接收通知(使用MQTT协议实现消息推送),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

现如今扫码付款和收款已经很便利了,比如超市买完东西付款时有两种方式 : 顾客可以出示付款码给收银员扫码收款,也可以由顾客扫描超市的收款码进行付款 。两种方式在付款完成后超市端会进行语音播报收到xx元。 前者收银员主动扫码,其使用的系统可以实时地收到付款反馈信息然后进行语音播报。后者顾客扫码付款则没有这么简单了,因为发起付款请求的是顾客端,收银端系统是不能实时地知道“我的二维码何时被扫了“,也就是说付款成功通知得由服务器推送过来,这里就涉及到了消息推送技术。

MQTT简介

MQTT (MQ Telemetry Transport)is a machine-to-machine (M2M)/“Internet of Things” connectivity protocol.
MQTT stands for MQ Telemetry Transport. It is a publish/subscribe, extremely simple and lightweight messaging protocol, designed for constrained devices and low-bandwidth, high-latency or unreliable networks. The design principles are to minimise network bandwidth and device resource requirements whilst also attempting to ensure reliability and some degree of assurance of delivery. These principles also turn out to make the protocol ideal of the emerging “machine-to-machine” (M2M) or “Internet of Things” world of connected devices, and for mobile applications where bandwidth and battery power are at a premium.
MQTT即MQ遥测传输协议,它是一个发布/订阅协议,非常简单和轻量级的消息传递协议,专为受限设备和低带宽、高延迟或不可靠的网络设计。设计原则是尽量减少网络带宽和设备资源需求,同时也试图确保可靠性和一定程度的交付保证。这些原则也使协议成为新兴的“机器对机器”(M2M)或“物联网”世界的理想连接设备,以及带宽和电池电力的移动应用程序。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。特性如下:
1、使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;
2、对负载内容屏蔽的消息传输;
3、使用 TCP/IP 提供网络连接;
4、有三种消息发布服务质量:

  • qos为0: “至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
  • qos为1:“至少一次”,确保消息到达,但消息重复可能会发生。
  • qos为2:“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。

5、小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;
6、使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。
MQTT官网
Android消息推送MQTT实战

MQTT Broker(代理服务器)

有很多开源的MQTT Broker代理服务器,参考MQTT Broker 选型进行Broker选型,下面简单介绍使用Mosquitto Broker。mosquitto官网下载

Mosquitto的windows下的安装与使用请参考:Windows环境下安装配置Mosquitto服务及入门操作介绍

注意:Mosquitto的安装目录的路径中不能包含空格(如C:Program Files),否则会出现意想不到的错误!!!

若要配置账号密码,需要在配置文件mosquitto.conf中加上下面两行代码:

#设置不允许匿名登录
allow_anonymous false
#设置账户密码文件位置(绝对路径)
password_file C:/MosquittoTest/pwfile.example

添加账号密码请使用mosquitto_passwd命令。参考 mosquitto_passwd man page
注意password_file需要跟文件的绝对路径,否则作为Windows服务后将无法启动。

Mosquitto Broker服务安装好后,启动它。默认监听端口时1883,需要在防火墙中添加入口规则,指定该端口允许通信。

服务器端安装Mosquitto Broker即可,不需要编写任何代码!!!!

客户端(测试工具及Android端)

客户端可以发布消息,也可以订阅消息。

客户端测试工具使用的是Eclipse paho,请自行下载 。Graphical MQTT Client Tools下载

安装Eclipse paho MQTT测试工具进行发布/订阅消息测试。

在这里插入图片描述
下面介绍Android端的实现。
github:eclipse paho.mqtt.android
gradle添加依赖

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


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

AndroidManifest清单中添加service

<application ....
	<service android:name="org.eclipse.paho.android.service.MqttService" />
</application>

MQTT辅助类MqttHelper.java

package com.jykj.hg.mqtt;

import android.util.Log;

import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
//
public class MqttHelper {
    public static final String TAG = MqttHelper.class.getSimpleName();
    /**
     *  clientId前缀,后面可以接userid
     */
    public static final String CLIENT_PREFIX="android_client_";//
    private static MqttClient client;
    private static MqttConnectOptions connectOptions;

    private static String mqtt_username="username";
    private static String mqtt_password="password";
    private static String mqtt_uri= "tcp://192.168.0.107:1883";

    /**
     * 完全配置,在应用程序启动的时候调用此方法进行初始化
     * @param _uri tcp://localhost:1883  or  ssl://localhost:8883
     */
    public static void initMqttConfig(String _uri,String _username,String _password){
        mqtt_uri =_uri;
        mqtt_username = _username;
        mqtt_password = _password;

    }
    //创建mqttClient对象,以及连接的对象
    public static MqttClient connect(String clientId) throws Exception {
        Log.e(TAG, "getMqttClient: "+mqtt_uri+","+mqtt_username+","+mqtt_password );
        if(client==null) client = new MqttClient(mqtt_uri,clientId,new MemoryPersistence());
        if(!client.isConnected()) client.connect(getOptions());
        return client;
    }
    public static MqttConnectOptions getOptions(){
        if(connectOptions!=null) return connectOptions;
        connectOptions = new MqttConnectOptions();
        connectOptions.setUserName(mqtt_username);
        connectOptions.setPassword(mqtt_password.toCharArray());
        connectOptions.setConnectionTimeout(6);
        //connectOptions.setCleanSession(false);
        return connectOptions;
    }
    public static MqttMessage getMessage(String msg){
        MqttMessage message = new MqttMessage();
        message.setQos(2);
        message.setRetained(false);
        message.setPayload(msg.getBytes());
        return message;
    }
    public static String getTopicRegular(int userid,String ccbh,String fcrq){
        return userid+"/"+ccbh+"_"+fcrq;
    }
    public static String getClientId(int userid){
        return CLIENT_PREFIX+userid;
    }
}

Activity中的使用TestActivity.java ,布局文件中就一个输入框和发布消息按钮

public class TestActivity extends AppCompatActivity {
	 private static final String TAG="TestActivity";
    private EditText etMsg;
    private MqttClient client;
     private Handler mhandler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bk_meal);
        final EditText etMsg = findViewById(R.id.etMsg);

        findViewById(R.id.btnPub).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                publish(etMsg.getText().toString());
            }
        });
        //连接并订阅消息,需要开启子线程,否则会阻塞主线程
        new Thread(){
            @Override
            public void run() {
                try {
                    client = MqttHelper.connect(MqttHelper.getClientId(5));
                    subscribeTopic();
                    client.setCallback(new MqttCallback() {
                        @Override
                        public void connectionLost(Throwable cause) {
                            Log.i(TAG, "connection lost");
                            subscribeTopic();
                        }
                        @Override
                        public void messageArrived(String topic, MqttMessage message) throws Exception {
                            Log.i(TAG, "received topic : " + topic+","+message);
                            //使用Handler将消息拉回到主线程,更新UI
                            final String msg = message.toString();
                            mhandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    //Update UI
                                    /* ...Update UI code .... */
                                    Toast.makeText(TestActivity.this,"收到消息:"+msg,Toast.LENGTH_SHORT).show();
                                }
                            });
                        }
                        @Override
                        public void deliveryComplete(IMqttDeliveryToken token) {
                            Log.i(TAG, "deliveryComplete");//消息发布成功
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                    mhandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            subscribeTopic();
                        }
                    },1000*30);//30秒后重新订阅
                }
            }
        }.start();
    }
     private void publish(final String msg){
        new Thread(){
            @Override
            public void run() {
                try {
                    client = MqttHelper.connect(MqttHelper.getClientId(5));
                    MqttMessage message = MqttHelper.getMessage(msg);
                    client.publish("testTopic", message);
                    Log.e(TAG, "mqtt publish: "+msg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
    private void subscribeTopic(){
        new Thread(){
            @Override
            public void run() {
             try {
                String topic = "testTopic";
                client = MqttHelper.connect(MqttHelper.getClientId(5));
                client.subscribe(topic);
                Log.e(TAG, "订阅主题: "+topic);
             } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

可以让EClipse paho MQTT测试工具作为一个客户端与Android进行测试。

注意:在收到消息回调函数中需要使用Handler.post()方法将消息拉回到主线程,然后更新UI界面等操作。另外发布/订阅操作需要开启子线程,否则会阻塞主线程!

上面的测试端调通了后,实现二维码被扫接收通知就不难了。这里topic主题的规则很重要,一般用‘username/boo/foo’的形式来定义主题,username保证在系统中是用户的唯一标识,这样可以精准的发布消息给订阅者。例如B客户端订阅了主题"B/scanpay" ,A客户端扫描B客户端出示的二维码,业务操作成功后(如付款)发布一个topic为 “B/scanpay” 的消息, 消息内容为 “pay 30 元 success”,然后B客户端由于订阅了该主题就会收到该消息,然后就可以进行相关操作(如语音播报).

Topic主题定义

topic可以包含通配符 + or #

+通配符可以用作单个层次结构的通配符,例如主题"a/b/c/d", 下面的订阅可以匹配:

a/b/c/d
+/b/c/d
a/+/c/d
a/+/+/d
+/+/+/+

而下面的订阅将不匹配:

a/b/c
b/+/c/d
+/+/+

#可以用作所有剩余层次结构的通配符,这意味着它必须是订阅中的最后一个字符。对于主题"a/b/c/d", 下面的订阅可以匹配:

a/b/c/d
#
a/#
a/b/#
a/b/c/#
+/b/c/#

长度为零的主题级别是有效的,这可能会导致一些稍微不明显的行为。例如,“a//topic”的主题可以与“a/+/topic”的订阅正确匹配。同样,在主题字符串的开头和结尾都可以存在零长度的主题级别,因此“/a/topic”将与“+/a/topic”、“#”或“/#”订阅相匹配,而“a/topic/”将与“a/topic/+”或“a/topic/#”订阅相匹配。

最后

以上就是不安烤鸡为你收集整理的Android 二维码被扫后接收通知(使用MQTT协议实现消息推送)的全部内容,希望文章能够帮你解决Android 二维码被扫后接收通知(使用MQTT协议实现消息推送)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部