这里写目录标题

  • 关于MQTT协议
  • Android端实现:
  • 1.在module中的build.gradle中添加以下依赖
  • 2.在AndroidManifest.xml中添加权限
  • 3.在 AndroidManifest.xml 注册Service
  • 4.创建TqMqttService服务
  • 5.Mqtt消息回调
  • 6.创建TimingService服务
  • 7.创建MQttMessage实体类,用于传送主题消息
  • 8.开启服务


关于MQTT协议

  MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议。它是一种发布/订阅,极其简单和轻量级的消息传递协议,专为受限设备和低带宽,高延迟或不可靠的网络而设计。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境。相对于XMPP,MQTT更加轻量级,并且占用的宽带低。

MQTT协议有以下特点:

  1. 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
  2. 对负载内容屏蔽的消息传输。
  3. 使用 TCP/IP 提供网络连接。
  4. 有三种消息发布服务质量:
  1. qos为0:“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
  2. qos为1:“至少一次”,确保消息到达,但消息重复可能会发生。这一级别可用于如下情况,你需要获得每一条消息,并且消息重复发送对你的使用场景无影响。
  3. qos为2:“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
  1. 小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量。
    使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。

Android端实现:

1.在module中的build.gradle中添加以下依赖

dependencies {
    /**
     * Gson解析
     */
    api 'com.google.code.gson:gson:2.8.2'
    /**
     * MQTT
     */
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0'
}

2.在AndroidManifest.xml中添加权限

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

3.在 AndroidManifest.xml 注册Service

<service android:name="org.eclipse.paho.android.service.MqttService"/>
<service android:name=".utils.TqMqttService"/>
<service android:name=".utils.TimingService"/>

4.创建TqMqttService服务

public class TqMqttService extends Service{

    public static final String TAG = TqMqttService.class.getSimpleName();
    public String HOST = "tcp://192.168.0.000:1883";
    public String nameUser = "nameUser";
    public String passWord = "passWord";

    private static int Qos = 2;

    private static MqttAndroidClient mqttAndroidClient;

    public String CLIENTID = String.valueOf(new Date().getTime());//客户端标识(唯一性)
    private MqttConnectOptions mMqttConnectOptions;

    //将全部订阅接口存入数组
    private String[] response = {
            ConfigPort.RESPONSE_DEV_STATUS,
            ConfigPort.RESPONSE_POWER_STATUS,
            ConfigPort.RESPONSE_GET_ETHCFG,
            ConfigPort.RESPONSE_SET_ETHCFG
    };

    //MQTT是否连接成功的监听
    private IMqttActionListener iMqttActionListener = new IMqttActionListener() {
        @Override
        public void onSuccess(IMqttToken arg0) {
            Log.i(TAG, "Mqtt连接成功");
            try {
                //可先将订阅接口全部订阅,后期只需要进行主题消息发送
                for (int i = 0; i < response.length; i++) {
                    mqttAndroidClient.subscribe(response[i], Qos);
                }
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(IMqttToken arg0, Throwable arg1) {
            arg1.printStackTrace();
            Log.i(TAG, "Mqtt连接失败!");
            doClientConnection();//连接失败,重连(可关闭服务器进行模拟)
        }
    };

    //订阅主题的回调
    private MqttCallback mqttCallback = new MqttCallback() {
        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            String result = new String(message.getPayload());
            Log.i(TAG, "Mqtt收到消息: " + result);
            //收到消息后进行响应
            sMqttData.onResult(result);
        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken arg0) {
            Log.i(TAG, arg0.isComplete() ? "消息发送成功" : "消息发送失败");
        }

        @Override
        public void connectionLost(Throwable arg0) {
            Log.i(TAG, "连接断开 ");
            Toast.makeText(getApplicationContext(), "Mqtt连接断开,正在重连!", Toast.LENGTH_LONG).show();
            doClientConnection();//连接断开,重连
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        init();
    }

    /**
     * 开启服务
     */
    public static void startService(Context mContext) {
        mContext.startService(new Intent(mContext, TqMqttService.class));
    }

    /**
     * 发布 (向服务器发送消息)
     *
     * @param topic   主题
     * @param message 消息
     */
    public static void publish(String topic, String message) {
        Integer qos = Qos;
        Boolean retained = false;
        try {
            //参数分别为:主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息
            if (mqttAndroidClient != null) {
                mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
            }
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化
     */
    private void init() {
        String serverURI = HOST; //服务器地址(协议+地址+端口号)
        mqttAndroidClient = new MqttAndroidClient(this, serverURI, CLIENTID);
        mqttAndroidClient.setCallback(mqttCallback); //设置监听订阅消息的回调
        mMqttConnectOptions = new MqttConnectOptions();
        mMqttConnectOptions.setCleanSession(true); //设置是否清除缓存
        mMqttConnectOptions.setConnectionTimeout(10); //设置超时时间,单位:秒
        mMqttConnectOptions.setKeepAliveInterval(10); //设置心跳包发送间隔,单位:秒
        mMqttConnectOptions.setAutomaticReconnect(true); // 设置自动重连
        mMqttConnectOptions.setUserName(nameUser); //设置用户名
        mMqttConnectOptions.setPassword(passWord.toCharArray()); //设置密码

        if (mqttAndroidClient != null) {
            doClientConnection();
        }

    }

    /**
     * 注册订阅
     * 订阅就是被动接受(接受另一个设备发送的topic)
     */
    public static void subscribes(String response) {
        try {
            Integer qos = 2;
            //订阅查询的主题、服务质量
            Log.i(TAG, "订阅:"+ response);
            if (mqttAndroidClient != null) {
                mqttAndroidClient.subscribe(response, qos.intValue());
            }
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    /**
     * 连接MQTT服务器
     */
    private void doClientConnection() {
        if (!mqttAndroidClient.isConnected() && isConnectIsNomarl()) {
            try {
                mqttAndroidClient.connect(mMqttConnectOptions, null, iMqttActionListener);
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 判断网络是否连接
     */
    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, "当前网络名称:" + name);
            return true;
        } else {
            Log.i(TAG, "没有可用网络");
            /*没有可用网络的时候,延迟3秒再尝试重连*/
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    doClientConnection();
                }
            }, 3000);
            return false;
        }
    }

    public static boolean isConnected() {
        if (mqttAndroidClient != null) {
            return mqttAndroidClient.isConnected();
        }
        return false;
    }

    @Override
    public void onDestroy() {
        try {
            mqttAndroidClient.disconnect(); //断开连接
        } catch (MqttException e) {
            e.printStackTrace();
        }
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    public static MqttData sMqttData;

    public static void setMqttData(MqttData mqttData) {
        sMqttData = mqttData;
    }
}

5.Mqtt消息回调

public interface MqttData {
    void onResult(String json);
}

6.创建TimingService服务

public class TimingService extends Service {

    private String TAG = TimingService.class.getSimpleName();

    private int TIME_INTERVAL = 1000 * 60 * 1;//设置三分钟执行一次
    private PendingIntent mPendingIntent;
    private AlarmManager mAlarmManager;
    public static final String TEST_ACTION = "timinService";

    @Override
    public void onCreate() {
        super.onCreate();
        IntentFilter intentFilter = new IntentFilter(TEST_ACTION);
        registerReceiver(receiver, intentFilter);
        mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent intent = new Intent();
        intent.setAction(TEST_ACTION);
        mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0低电量模式需要使用该方法触发定时任务
            mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), mPendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4以上 需要使用该方法精确执行时间
            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), mPendingIntent);
        } else {//4.4一下 使用老方法
            mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, mPendingIntent);
        }
    }

    public static void startService(Context mContext) {
        mContext.startService(new Intent(mContext, TimingService.class));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (TEST_ACTION.equals(action)) {
                Log.i(TAG, "定时任务执行");
                sendReadStatusOrder();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, mPendingIntent);
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, mPendingIntent);
                }
            }
        }
    };

    /**
     * 发送指令读取
     */
    public void sendReadStatusOrder() {
        try {
            for (int i = 3; i < 11; i++) {
                int timeNumber = 1000 * (i - 3);
                Message msg = new Message();
                msg.what = 0;
                msg.obj = "指令";
                sendHandler.sendMessageDelayed(msg, timeNumber);
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

    Handler sendHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            String result = msg.obj.toString();
            Log.i(TAG, "Handler result: " + result);
        }
    };

}

7.创建MQttMessage实体类,用于传送主题消息

public class MQttMessage {

    /*{
        "token": 123,
        "timestamp": "2023-4-21-16:07:00",
    }*/

    private int token;
    private String timestamp;

    public MQttMessage(int token, String timestamp) {
        this.token = token;
        this.timestamp = timestamp;
    }

    public int getToken() {
        return token;
    }

    public void setToken(int token) {
        this.token = token;
    }

    public String getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }
}

8.开启服务

public class MainActivity extends AppCompatActivity {

	@BindView(R.id.tv_temp)
    TextView tvTemp;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TqMqttService.startService(mContext);//开启服务
        TimingService.startService(mContext);
        //点击获取消息
        tvTemp.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                TqMqttService.publish(ConfigPort.REQUEST_DEV_STATUS,
                    new Gson().toJson(new MQttMessage(2, DataUtils.getCurrentTime(0))));
                //消息回调    
                TqMqttService.setMqttData(json -> {
                	//将json数据通过Gson存入DevStatusBean
                	DevStatusBean mStatusBean = new Gson().fromJson(json, DevStatusBean.class);
            	});
            }
        });
    }
}