采用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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/460657.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【矩阵】73. 矩阵置零【中等】

矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 解题思路 1、…

java数据结构与算法刷题-----LeetCode51. N 皇后

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 解题思路&#xff1a;时间复杂度O( N ! N! N!)&#xff0c;空间复…

【IC设计】Verilog线性序列机点灯案例(一)(小梅哥课程)

文章目录 设计目标思路仿真结果时间点一&#xff1a;201ns时间点二&#xff1a;220ns时间点三&#xff1a;250,000,220ns时间点四&#xff1a;1,000,000,200ns时间点五&#xff1a;1,000,000,220ns 总结&#xff1a; 案例和代码来自小梅哥课程&#xff0c;本人仅对知识点做做笔…

Centos7安装ffmpeg

Centos7安装ffmpeg 用到的包压缩并安装 用到的包 压缩并安装 tar xvJf ffmpeg-5.0.1.tar.xz yum install -y gcctar -zxvf yasm-1.3.0.tar.gz cd yasm-1.3.0 ./configure make && make install yasm --versionyum install -y bzip2tar jxvf nasm-2.14.02.tar.bz2 cd n…

海格里斯HEGERLS托盘搬运机器人四向车引领三维空间集群设备柔性运维

随着市场的不断迅猛发展变化&#xff0c;在物流仓储中&#xff0c;无论是国内还是海外&#xff0c;都对托盘式解决方案需求量很大。顾名思义&#xff0c;托盘式解决方案简单理解就是将产品放置在托盘上进行存储、搬运和拣选。 面对托盘式方案需求&#xff0c;行业中常见的方案是…

git报: “fatal: detected dubious ownership in repository“

“fatal: detected dubious ownership in repository”的中文翻译是&#xff1a;“致命错误&#xff1a;检测到仓库中存在可疑的所有权问题”。 这句话意味着 Git 在检查代码仓库时发现所有权存在问题&#xff0c;可能是由于文件或目录的所有权与 Git 仓库预期的所有权不匹配。…

React18 后台管理模板项目:现代、高效与灵活

&#x1f389; 给大家推荐一款React18TypescriptVitezustandAntdunocss且超级好用的中后台管理框架 项目地址 码云&#xff1a;https://gitee.com/nideweixiaonuannuande/xt-admin-react18github&#xff1a;https://github.com/1245488569/xt-admin-react18 演示地址 http…

(done) 解释 python3 torch.utils.data DataLoader

特别注意&#xff1a;DataLoader 返回的迭代器是无尽的&#xff0c;依据如下 (CHATGPT3.5) DataLoader 返回的迭代器默认情况下是无尽的&#xff0c;因为它会无限地循环遍历数据集&#xff0c;以提供批量的数据。在训练神经网络时&#xff0c;通常会使用无尽的迭代器来循环遍历…

内存操作函数(C语言)

目录 memcpy使用和模拟实现 memcpy函数的模拟实现 memmove的使用和模拟实现 memmove的模拟实现 memset函数的使用 memcmp函数的使用 memcpy使用和模拟实现 mem--memory--记忆--内存 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置这…

Android Studio实现内容丰富的安卓校园二手交易平台

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 项目编号038 1.开发环境android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.查看二手商品列表 3.查看二手商品详情 4.评论商品&…

Window11 下 git报: “fatal: detected dubious ownership in repository“

Window11 下 git报: “fatal: detected dubious ownership in repository” 一般是因为重装了系统或更换了用户, git文件夹的所有者发生了改变 可以右键点文件夹 属性 &#x1f449; 安全 &#x1f449; 高级 点完 高级,新对话框点 更改 点完 更改 新对话框点 高级 点完 高级…

【JavaEE -- 多线程3 - 多线程案例】

多线程案例 1.单例模式1.1 饿汉模式的实现方法1.2 懒汉模式的实现方法 2. 阻塞队列2.1 引入生产消费者模型的意义&#xff1a;2.2 阻塞队列put方法和take方法2.3 实现阻塞队列--重点 3.定时器3.1 定时器的使用3.2 实现定时器 4 线程池4.1 线程池的使用4.2 实现一个简单的线程池…

力扣大厂热门面试算法题 33-35

33. 搜索旋转排序数组&#xff0c;34. 在排序数组中查找元素的第一个和最后一个位置 &#xff0c;35. 搜索插入位置&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.15 可通过leetcode所有测试用例。 目录 33. 搜索旋转排序数组…

Java项目:52 springboot基于SpringBoot的旅游网站的设计与实现013

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 旅游网站主要功能如下&#xff1a; 1.用户管理&#xff1a;注册、登录、退出、修改密码&#xff1b; 2.分类显示&#xff1a;显示旅游路线的分类&am…

基础GamePlay知识-凸多边形碰撞检测(SAT)

分离轴算法 也称为SAT(Separating Axis Theorem)算法&#xff0c;主要用于凸多边形之间的相交检测&#xff0c;主要思路为寻找分离轴。 分离轴&#xff1a;分离轴是一个向量&#xff0c;可以理解为一条平行于多边形边的线。如果两个凸多边形在分离轴上的投影没有重叠&#xf…

基于单片机的酒精浓度测试仪

摘 要 现如今&#xff0c;人们对生活的态度和生活方式变得不同,&#xff0c;不仅私家车成为了人们最普遍的交通工具&#xff0c;大多数人都有自己的私家车,而且人们对酒精的消耗量也越来越大&#xff0c;这些就导致酒后驾车行为越来越普遍&#xff0c;酒后驾车意外越来越频繁&…

家电工厂5G智能制造数字孪生可视化平台,推进家电工业数字化转型

家电5G智能制造工厂数字孪生可视化平台&#xff0c;推进家电工业数字化转型。随着科技的飞速发展&#xff0c;家电行业正迎来一场前所未有的数字化转型。在这场制造业数字化转型中&#xff0c;家电5G智能制造工厂数字孪生可视化平台扮演着至关重要的角色。本文将从数字孪生技术…

NCP1271D65R2G中文资料规格书PDF数据手册引脚图参数图片价格功能特性描述

产品描述&#xff1a; NCP1271 是成功的 7 引脚电流模式 NCP12XX 系列的新一代引脚-引脚兼容新产品。该控制器通过使用可调节 Soft Skip 模式和集成的高电压启动 FET&#xff0c;实现了卓越的待机功耗。此专属 Soft Skip 还大大降低了噪音的风险。 因此可以在箝位网络中使用不…

我的尝试:Codigger + Vim

若您愿意耐心投入&#xff0c;学习 Vim 的过程其实远比想象中轻松。我对 Vim 产生兴趣&#xff0c;主要是源于它对提升生产力的巨大潜力。我尝试了 Neovim、NvChad 以及 Codigger Vim 插件&#xff0c;如今我的工作效率已远超从前。 那么&#xff0c;Vim 究竟是什么呢&#xff…

uni app 钓鱼小游戏

最近姑娘喜欢玩那个餐厅游戏里的钓鱼 &#xff0c;经常让看广告&#xff0c;然后就点点点... 自己写个吧。小鱼的图片自己搞。 有问题自己改&#xff0c;不要私信我 <template><view class"page_main"><view class"top_linear"><v…