基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

目录

写在前面

前提准备 

本次干的事情——编写安卓app

正文

比如 子线程定时获取数据库信息实时更新UI

 比如子线程判断主线程事件进行数据操作实现对设备的控制


写在前面

为实现第三方移动端控制的要求,我需要实现一个在个人局域网内控制设备的移动端APP。

前提准备 

一个已经实现好通信控制的硬件和相关外设(单片机)。

一块stm32主控板,若干块通信模块(esp32,lora模块)。

一个emqx的mqtt消息服务器。

一个自己写网络接口的java客户端(一个基于springboot整合mybatis的后端服务器,提供命令调用的接口和监听硬件采集的数据并转储于数据库)。

这个java客户端连接mqtt服务器,订阅数据获取的主题,发布对应主题的消息以控制硬件。

在以上的前提都准备后,理论上我已经干的差不多的,安卓只是一个写界面来更好地服务与我控制的操作而已。。。。我可以在网页上直接访问java客户端提供的接口,来访问硬件采集的数据,控制硬件的外设,以及干更多你想干的事情(比如RF高频卡感应来模拟门禁,通过发送的信息实现语音播报和语音控制等等)

本次干的事情——编写安卓app

我的想法就是越简单越好,这会让人更加地清晰和明白,我没有像网上一般的那样让安卓去连接

mqtt服务器,然后通过安卓与mqtt服务器直接打交道,(获取数据和命令控制)。但是,我只是想直接简单明了的调用接口,以最简单的http请求的方式实现我的目的。为了这个,java客户端干了所有的事,再提供访问的接口。

参考我的java客户端

局域网内用JAVA建立MQTT客户端监听MQTT服务器消息并持久化到数据库_昊月光华的博客-CSDN博客_java mqtt服务端

java客户端运行

基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

 提供接口(http请求)访问基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

发布命令

基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

当然安卓连接mqtt服务器也不是一件难事,问题在于假如我有多个应用程序都要访问数据和控制呢?哪不是每一个都得连接mqtt服务器,每一个都要写监听对应主题以及发布主题消息的方法?

当然,目前很多的外网云平台正是提供第三方访问的接口请求。也就是API

比如巴法云平台,华为云平台等等。

基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

正文

首先是编写界面和交互逻辑代码,最主要的也就是主线程与子线程的通信,也就是子线程获取数据,主线程更新界面。

这里有常见几种的情形:

  • 在一个页面内(activity)内,创建一个内部类(继承线程类,重写run方法),子线程不断地获取数据,然后通过runonUIthread 更新界面。
  • 主线程持有子线程的handler引用,子线程收到主线程发过来的message ,通过message.what判断是哪个控件发过来的,然后进行数据操作(访问数据库等等),数据操作完后,通过runonUIthread 更新UI界面。

比如 子线程定时获取数据库信息实时更新UI

在首页中

package com.example.yuezhenhao;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.yuezhenhao.HttpRequest.DAO;

import java.security.PrivateKey;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {

    private View exit,mainpage,control,chart,querypage;

    private TextView runt, temp, humi, ls,tl,hl,ll,state,isc;
    private  String TAG="MAIN";
    int i=0;
    private Handler mHandler= new Handler(Looper.getMainLooper()) {
        @Override
        //重写handleMessage方法,根据msg中what的值判断是否执行后续操作
        public void handleMessage(Message msg) {

            if (msg.what == 0) {
                i++;
                runt.setText(String.valueOf(i));


            }
        }
    };





    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //开启工作线程
        WorkThread workThread = new WorkThread();
        workThread.start();
         querypage=findViewById(R.id.querypage);



        init(); //初始化控件


        //转主页
        mainpage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e(TAG, "ToMainPage ");
            }
        });
        //转控制页面
        control.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e(TAG, "To Control");
                Intent intent = new Intent(MainActivity.this, Conrrol.class);
                startActivity(intent);

            }
        });

        //转图表页面
        chart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e(TAG, "To Chart");
                Intent intent = new Intent(MainActivity.this, DatesTable.class);
                startActivity(intent);
            }
        });
        //退出程序
        exit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //退出整个应用程序
                Log.e(TAG, "exit" );
                finishAffinity();
                System.exit(0);

            }
        });

       // 转查询页面
        querypage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                Log.e(TAG, "query" );
                Intent intent = new Intent(MainActivity.this,  QuertDate.class);
                startActivity(intent);


            }
        });




    }




    /**
     * 初始化组件
     */
    public void init() {

        runt = findViewById(R.id.conf);
        temp = findViewById(R.id.temp);
        humi= findViewById(R.id.humi);
        ls = findViewById(R.id.ls);

        tl = findViewById(R.id.tl);
        hl= findViewById(R.id.hl);
        ll = findViewById(R.id.ll);
        isc=findViewById(R.id.isc);

        state=findViewById(R.id.state);
        state.setText("连接中");

        mainpage=findViewById(R.id.mainpage);
        control=findViewById(R.id.control);
        chart=findViewById(R.id.table);

        exit=findViewById(R.id.exit);


    }





//工作线程刷新主页数据
class WorkThread extends Thread {
        private  String _temp;
    private  String _humi;
    private  String _ls;

    //定义阈值
    private  String TempLimit;
    private  String HumiLimit;
    private  String LsLimit;
    private  String _isc; //是否自动控制

    private boolean cons=false;
    private   Handler workhandler;
    private  int time=0;
    public void run() {
        new Timer().schedule(new TimerTask() {
            @SuppressLint("ResourceAsColor")
            @Override
            public void run() {
                time++;
                JSONArray  limits= null;
                JSONObject LatestDates=null;
                try {
                    limits = DAO.GetLimit();
                    LatestDates=(JSONObject) DAO.GetLatest().get(0);

                    cons=true;
                    TempLimit=((JSONObject)limits.get(0)).getString("val");
                    HumiLimit=((JSONObject)limits.get(1)).getString("val");
                    LsLimit=((JSONObject)limits.get(2)).getString("val");
                    _isc=((JSONObject)limits.get(3)).getString("val");


                    _temp=LatestDates.getString("msg1")+"℃";
                    _humi=LatestDates.getString("msg2")+"%";
                    _ls=LatestDates.getString("msg3")+"lx";


                    if(_isc.equals("1")){
                        _isc="开启";
                    }
                    else
                        _isc="关闭";

                } catch (Exception e) {
                    e.printStackTrace();
                    cons=false;

                }



                MainActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        runt.setText(time+"s");
                        tl.setText(TempLimit);
                        hl.setText(HumiLimit);
                        ll.setText(LsLimit);
                        isc.setText(_isc);

                        temp.setText(_temp);
                        humi.setText(_humi);
                        ls.setText(_ls);


                        if(cons)state.setText("已连接");
                        else {
                            state.setText("断开连接");
                            state.setTextColor(R.color.red);
                        }
                    }
                });

            }
        }, 0, 1000);


    }
}


}







 基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

 

 比如子线程判断主线程事件进行数据操作实现对设备的控制

package com.example.yuezhenhao;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.StrictMode;
import android.text.Editable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.Toast;

import com.example.yuezhenhao.HttpRequest.DAO;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class Conrrol extends AppCompatActivity    {

    private Switch dev1,dev2,dev3,dev4;
    private Button msgbn1,msgbn2,msgbn3,pbt1,pbt2;
    private EditText msg1,msg2,msg3,mtor1,mtor2;
    private  boolean flag1=false;
    public  Handler mHandler;
    private  String TAG="CONTROL";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_conrrol);

        init();

        SubThread subThread=new SubThread();
        subThread.start();

        mHandler=new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                    if(msg.what == 1){
                        Toast.makeText(Conrrol.this, "操作成功", Toast.LENGTH_SHORT).show();
                    }
                    else
                        Toast.makeText(Conrrol.this, "操作失败", Toast.LENGTH_SHORT).show();
            }

        };

        //控制设备1-3的常规开和关
        dev1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if (dev1.isChecked()) {
                    Log.e(TAG, "ON DEV1");
                    PackageDate(subThread.subhandler,"dev1","20");
                }
                else {
                    Log.e(TAG, "OFF DEV1");
                    PackageDate(subThread.subhandler,"dev1","0");
                }
            }
        });

        dev2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if (dev2.isChecked()) {
                    Log.e(TAG, "ON DEV2");
                    PackageDate(subThread.subhandler,"dev2","20");

                }
                else {
                    Log.e(TAG, "OFF DEV2");
                    PackageDate(subThread.subhandler,"dev2","0");
                }
            }
        });

        dev3.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if (dev3.isChecked()) {
                    Log.e(TAG, "ON DEV3");
                    PackageDate(subThread.subhandler,"dev3","5000");
                }
                else {
                    Log.e(TAG, "OFF DEV3");
                    PackageDate(subThread.subhandler,"dev3","0");
                }
            }
        });

        //设置是否自动控制
        dev4.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if (dev4.isChecked()) {
                    Log.e(TAG, "自动控制开启");
                    PkLimitAndIsc(subThread.subhandler,"4","1");
                } else {
                    Log.e(TAG, "自动控制关闭");
                    PkLimitAndIsc(subThread.subhandler,"4","0");
                }
            }
        });

        //给字段msg1设置阈值
        msgbn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String val=msg1.getText().toString();
                PkLimitAndIsc(subThread.subhandler,"1",val);

            }
        });
        //给字段msg2设置阈值
        msgbn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String val=msg2.getText().toString();
                PkLimitAndIsc(subThread.subhandler,"2",val);
            }
        });
        //给字段msg3设置阈值
        msgbn3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String val=msg3.getText().toString();
                PkLimitAndIsc(subThread.subhandler,"3",val);
            }
        });

        //电机1控制, 支持PWM和正反转
        pbt1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String val=mtor1.getText().toString();
                PackageDate(subThread.subhandler,"dev1",val);
            }
        });
        //电机2控制, 支持PWM和正反转
        pbt2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String val=mtor2.getText().toString();
                PackageDate(subThread.subhandler,"dev2",val);
            }
        });




    }

    /**
     * 基于不同id的按钮控件打包数据
     * 数据:控制执行部件的数据
     */
    public  void PackageDate(Handler handler,String name,String val){

    Map[] li=new Map[2];
    try {
        li[0]=new HashMap();
        li[1]=new HashMap();
        li[0].put("name",name);
        li[1].put("val",val);
        Message msg=Message.obtain();
        msg.what=1;
        msg.obj=li;
        handler.sendMessage(msg);
    } catch (Exception e) {
        e.printStackTrace();
    }

}

    /**
     * 基于不同id的按钮控件打包数据
     * 数据:设置阈值的数据或者是是否自动控制的标记
     */
    public  void PkLimitAndIsc(Handler handler,String name,String val){

        Message msg=Message.obtain();
        Object[] obj=new Object[2];
        obj[0]=new Object();
        obj[1]=new Object();
        obj[0]=name;
        obj[1]=val;
        msg.what=2;
        msg.obj=obj;
        handler.sendMessage(msg);

    }

    /**
     * 初始化控件
     */
    public void init(){
        dev1=findViewById(R.id.dev1);
        dev2=findViewById(R.id.dev2);
        dev3=findViewById(R.id.dev3);
        dev4=findViewById(R.id.dev4);

        msg1=findViewById(R.id.msg1);
        msg2=findViewById(R.id.msg2);
        msg3=findViewById(R.id.msg3);

        msgbn1=findViewById(R.id.msg_bn1);
        msgbn2=findViewById(R.id.msg_bn2);
        msgbn3=findViewById(R.id.msg_bn3);

        pbt1=findViewById(R.id.pbn1);
        pbt2=findViewById(R.id.pbn2);

        mtor1=findViewById(R.id.mtor1); //两个电机的可编辑框的值的获取
        mtor2=findViewById(R.id.mtor2);


    }



    //子线程协调主线程发起网络请求
    class SubThread extends  Thread {
        public Handler subhandler;

        public void run() {
            Looper.prepare();
            subhandler = new Handler(Looper.myLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case 1:
                            //解包发起请求
                            Map[] li= (Map[]) msg.obj;

                            try {
                                DAO.ControlDec((String) li[0].get("name"),(String) li[1].get("val"));
                                Log.e(TAG, "success" );
                                mHandler.sendEmptyMessage(1);
                            } catch (Exception e) {
                                e.printStackTrace();
                                mHandler.sendEmptyMessage(0);
                            }
                            break;
                        case 2:
                            try {
                                Object[] objects= (Object[]) msg.obj;
                                DAO.SetLimitAndIsc(objects[0].toString(),objects[1].toString());
                                Log.e(TAG, "success" );
                                mHandler.sendEmptyMessage(1);
                            } catch (Exception e) {
                                e.printStackTrace();
                                mHandler.sendEmptyMessage(0);
                            }
                            break;

                        default:
                            break;

                    }
                }

            };
            Looper.loop();

        }
    }

}

基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

 

这两点是最主要的,另外就是数据统计,比如画折线图对数据进行实时显示,还有对历史数据(传感器信息)进行查询等等。

基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

 

 基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

 

参考

Android的handler消息收发处理——子线程与主线程(UI线程)间的通信_昊月光华的博客-CSDN博客_android获取主线程handler

 

然后就是界面布局,我不追求美观,只要能用就行。

本文章来源于互联网,如有侵权,请联系删除!原文地址:基于局域网内物联网的androidAPP开发——实现消息上报与命令下达

相关推荐: 阿里云物联网平台实现MQTT通信

阿里云物联网平台实现MQTT通信 一、环境搭建 一、阿里云物联网平台 二、MQTT.fx 总结 一、环境搭建 一、阿里云物联网平台 1.首先进入阿里云官网https://www.aliyun.com/?utm_content=se_1000301881注册并登…