采用MQTT协议实现Android APP与阿里云平台的连接

前言

相信APP+单片机是很多同学毕设或者课设的模式,上学期做课设的时候用到了MQTT协议连接阿里云平台实现数据的通信,也是根据网上大佬的经验做的,中间也踩了很多坑。本文将介绍Android APP 通过MQTT协议与阿里云云平台连接的内容,希望对大家能有所帮助。


一、MQTT原理

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议MQTT最大优点在于,用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。
MQTT使用的发布/订阅消息模式,它提供了一对多的消息分发机制,从而实现与应用程序的解耦。
这是一种消息传递模式,消息不是直接从发送器发送到接收器(即点对点),而是由MQTT Broker分发的。
MQTT的工作模式
在设计中,阿里云服务器作为一个消息中转站,即
下位机→MQTT Broker→云平台(流转)→MQTT Broker →上位机

二、创建APP

提示:以下非零基础可以跳过
1.创建APP项目,选择Empty Activity
在这里插入图片描述
在这里插入图片描述

2.设置UI
创建完后会有一个空白的Activity,找到res/layout目录下的的activity_main.xml文件,在这里编写UI。
这里我的UI界面仅供参考,因为项目还有其他功能,这里只讲述MQTT的连接,不需要的BUTTON控件可以自行删除。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#078307"
        android:orientation="vertical">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="78dp"
            android:layout_marginTop="30dp"
            android:background="@color/green">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:layout_marginTop="10dp"
                android:gravity="center"
                android:text="基于人脸识别的小区门禁系统"
                android:textColor="@color/white"
                android:textSize="24sp" />
        </RelativeLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#FAF6F6"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:gravity="center">

                <ImageView
                    android:id="@+id/m_im_1"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:layout_marginLeft="10dp"
                    android:layout_marginRight="10dp"
                    android:scaleType="fitXY"
                    android:src="@drawable/img" />
            </LinearLayout>


            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:layout_marginTop="30dp"
                android:orientation="vertical">

                <RelativeLayout
                    android:layout_width="match_parent"

                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                    <TextView
                        android:layout_width="325dp"
                        android:layout_height="60dp"
                        android:layout_centerInParent="true"
                        android:layout_marginBottom="20dp"
                        android:background="@drawable/login_shape"
                        android:gravity="center"
                        android:text="请选择验证方式"
                        android:textColor="@color/white"
                        android:textSize="30dp" />
                </RelativeLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="220dp"
                    android:gravity="center"
                    android:orientation="vertical">

                    <Button
                        android:id="@+id/Rfid"
                        android:layout_width="150dp"
                        android:layout_height="40dp"
                        android:background="@drawable/button_selector"
                        android:text="RFID刷卡验证"
                        android:textColor="@color/white" />

                    <Button
                        android:id="@+id/face_recognition"
                        android:layout_width="150dp"
                        android:layout_height="40dp"
                        android:layout_marginTop="15dp"
                        android:background="@drawable/button_selector"
                        android:text="人脸验证"
                        android:textColor="@color/white" />

                    <Button
                        android:id="@+id/password_check"
                        android:layout_width="150dp"
                        android:layout_height="40dp"
                        android:layout_marginTop="15dp"
                        android:background="@drawable/button_selector"
                        android:text="密码验证"
                        android:textColor="@color/white" />
                    <Button
                        android:id="@+id/exit"
                        android:layout_width="150dp"
                        android:layout_height="40dp"
                        android:layout_marginTop="15dp"
                        android:background="@drawable/button_selector"
                        android:text="退出"
                        android:textColor="@color/white" />
                </LinearLayout>


            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="25dp">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="MQTT连接状态:"
                    android:textColor="@color/black"
                    android:textSize="16sp" />

                <TextView
                    android:id="@+id/m_mqtt"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text=" "
                    android:textColor="@color/black"
                    android:textSize="16sp" />
            </LinearLayout>


        </LinearLayout>


    </LinearLayout>

</LinearLayout>

3.添加mqtt包的依赖
在项目的build.gradle 中添加一行implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0'
如下图
在这里插入图片描述
4.联网权限配置
在AndroidManifest.xml文件中添加`

<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> `

在这里插入图片描述

到这里Android项目就创建完成啦

三、创建阿里云产品

登录阿里云平台后,根据指示找到物联网平台
在这里插入图片描述
进入后创建产品,有一个30天免费试用的产品,建议把支付宝的自动续费关闭。进入后就可以开始创建产品,具体产品的参数设置已经记不清了,当时的产品也已经被回收了,可以参考一下别的博客。
在这里插入图片描述
产品创建完成后需要创建设备,这里需要创建一个上位机和下位机,这里下位机用的是树莓派。
在这里插入图片描述
设备创建完成后在设备信息中会给出MQTT的连接参数,需要我们在Android APP和树莓派代码中进行配置。
在这里插入图片描述
在这里设备就创建好了,但是还没完成,还需要配置解析器。
这里需要用到的是云产品流转。
这里在云产品流转功能中创建了两个解析器,绑定数据源和数据目的,并编写解析器的脚本。数据源和数据目的根据解析器ID确定,解析器脚本即读取转发的数据。注意,解析器只能识别JSON格式数据和二进制,这里推荐使用JSON格式。
示例脚本如下:

var data = payload("json");
writeIotTopic(1004,"/ionmnFFCKzs/shumeipai/user/get",data);

在这里插入图片描述
通过payload函数,获取设备上报的消息内容,并按照JSON格式转换。
然后将消息发送给树莓派接收的Topic。
通过日志服务可以监控到消息转发的过程,如下图所示:
在这里插入图片描述
这里消息质量使用的是Qos1,至少保证传一次。
数据流如下:
在这里插入图片描述

四、编写代码

现在到了编写代码部分,如果你的功能较多,需要跳转页面,建议把MQTT挂在后台,防止切屏的时候MQTT连接断开,从而导致数据丢包。
首先是配置MQTT的参数配置,根据上文中MQTT配置信息复制粘贴过去。
添加代码全面飘红?不要慌,因为你还没有完成构建,很多类还没导入,逐个alt+enter导入即可。

    private Handler handler;
    private SqliteDBHelper mHelper;
    private MqttClient client;
    private final String host = "";
    private final String userName = "";
    private final String passWord = "";
    private final String ClientId = "";
    private final String mqtt_sub_topic = "";//订阅话题
    private final String mqtt_pub_topic = "";//发布话题
    private MqttConnectOptions mqttConnectOptions;
protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        Objects.requireNonNull(getSupportActionBar()).hide();//这种方式默认式亮色主题
        setContentView(R.layout.activity_main_desk);
        TextView m_mqtt = findViewById(R.id.m_mqtt);//获取控件,不需要的控件自行删除
        Button Rfid = findViewById(R.id.Rfid);
        Button face_recognition = findViewById(R.id.face_recognition);
        Button password_check = findViewById(R.id.password_check);
        Button exit = findViewById(R.id.exit);
        Mqtt_init();
        startReconnect();
        handler = new Handler(Looper.myLooper()) {
    
    //用于处理
            @SuppressLint("SetTextI18n")
            public void handleMessage(Message msg) {
    
    
                super.handleMessage(msg);
                switch (msg.what) {
    
    
                    case 1: //开机校验更新回传
                        break;
                    case 2:  // 反馈回传
                        break;
                    case 3:  //MQTT 收到消息回传
                        System.out.println(msg.obj.toString());   // 显示MQTT数据
                        break;
                    case 31:   //连接成功
                        m_mqtt.setText("连接成功");//连接成功后按钮变为可点击状态
                        Rfid.setOnClickListener(MainDeskActivity.this);
                        face_recognition.setOnClickListener(MainDeskActivity.this);
                        password_check.setOnClickListener(MainDeskActivity.this);
                        exit.setOnClickListener(MainDeskActivity.this);

                        try {
    
    
                            client.subscribe(mqtt_sub_topic, 1);//订阅
                        } catch (MqttException e) {
    
    
                            e.printStackTrace();
                        }
                        break;
                    case 30:  //连接失败
                        Toast.makeText(MainDeskActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
                        m_mqtt.setText("连接失败");
                        break;

                    default:
                        break;
                }
            }
        };
    }

Mqtt_init函数
需要注意的一点是如果你的MQTT连接断开后就无法重连上,建议将会话心跳设置长一点

private void Mqtt_init() {
    
    
        try {
    
    
            client = new MqttClient(host, ClientId, new MemoryPersistence());
            //MQTT的连接设置
            mqttConnectOptions = new MqttConnectOptions();
            mqttConnectOptions.setCleanSession(false);
            mqttConnectOptions.setUserName(userName);
            mqttConnectOptions.setPassword(passWord.toCharArray());
            mqttConnectOptions.setConnectionTimeout(10);
            
            // 设置会话心跳时间 单位为秒 服务器会每隔1.5*30秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
            //由于自身网络延时很难确定,建议设大一点,防止断开连接后无法重连
            mqttConnectOptions.setKeepAliveInterval(30);
            //设置回调
            client.setCallback(new MqttCallback() {
    
    
                @Override
                public void connectionLost(Throwable cause) {
    
    
                    //连接丢失后,一般在这里面进行重连
                    System.out.println("connectionLost----------");
                    startReconnect();
                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken token) {
    
    
                    //publish后会执行到这里
                    System.out.println("deliveryComplete---------"
                            + token.isComplete());
                }

                @Override
                public void messageArrived(String topicName, MqttMessage message)
                        throws Exception {
    
    
                    //subscribe后得到的消息会执行到这里面
                    System.out.println("messageArrived----------");
                }
            });
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

    }
MQTT连接函数
    // MQTT连接函数
    private void Mqtt_connect() {
    
    
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    

                try {
    
    
                    if (!client.isConnected())  //如果还未连接
                    {
    
    
                        client.connect(mqttConnectOptions);
                        Message msg = new Message();
                        msg.what = 31;
                        handler.sendMessage(msg);
                    }
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                    e.getMessage();
                    Message msg = new Message();
                    msg.what = 30;
                    handler.sendMessage(msg);
                }

            }
        }).start();
    }

MQTT重连函数

// MQTT重新连接函数
    private void startReconnect() {
    
    
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (!client.isConnected()) {
    
    
                    Mqtt_connect();
                }
            }
        }0, 1000, 10 * 1000, TimeUnit.MILLISECONDS);
    }
MQTT发布订阅
    // 订阅函数    (下发任务/命令)
    private void publish_message_plus(String topic, String message2) {
    
    
        if (client == null || !client.isConnected()) {
    
    
            return;
        }
        MqttMessage message = new MqttMessage();
        message.setPayload(message2.getBytes());
        message.setQos(1);//设置消息质量
        //MQTT一共有三种消息质量
        //Qos0:会发生消息的丢失或重复
        //Qos1:至少送达一次
        //Qos2:保证只送达到目标端一次,网络开销最高
        try {
    
    
            client.publish(topic, message);
        } catch (MqttException e) {
    
    
            e.printStackTrace();
        }
    }

附上JSON数据的处理,其中message是MessageArrived中收到的数据,将message转为jsonObject对象后通过键获取值。
这里只用到了简单的处理。假设收到的数据是"{\"flag\":\"02\"}\n"

JSONObject jsonObject = new JSONObject(message.toString());
int flag = jsonObject.getString("flag");

五、完成测试

以上准备工作完成后就可以进行测试了,这里可以采用MQTT.fx软件来模拟下位机来发送信息,官网下载最新版http://mqttfx.org,由于只有三个月的免费试用,我的已经做不了演示了,详细操作可以自行网络搜索。联调成功后就可以对下位机进行配置了。
MQTT连接成功后如下图所示:
在这里插入图片描述
可以根据自己功能的需要添加点击事件以及消息处理的逻辑,有问题欢迎私信。

总结

本文主要讲述了利用MQTT协议实现Android app与阿里云平台的连接,流程并不难,新手会因为不熟悉而踩一些坑。在调试过程中,建议利用MQTT.fx分别对上位机和下位机调试,便于排查问题。消息接收失败可以在云平台上和MQTT.fx的日志里追踪消息。难点需要掌握Handler的回传机制,便于进行逻辑的编写。第一次开源,编写不易,如果帮到你的话可以点个赞吗。
参考资料

https://blog.csdn.net/x97666/article/details/125172129

猜你喜欢

转载自blog.csdn.net/m0_63208818/article/details/134935867