安卓MQTT协议的的使用,并使用protobuf协议传输数据

1,导入2个jar包

2,在AndroidManifest中添加Service

然后在代码中定义自己的Servce.例如我的叫:MQTTService

3,在我们服务启动的时候调用init方法进行初始化MQTT各种操作


    private void init() {
        String uri = host;
        client = new MqttAndroidClient(getApplicationContext(), uri, clientId);
        // 设置MQTT监听并且接受消息
        client.setCallback(mqttCallback);

        conOpt = new MqttConnectOptions();
        // 清除缓存
        conOpt.setCleanSession(true);
        // 设置超时时间,单位:秒
        conOpt.setConnectionTimeout(20);
        // 心跳包发送间隔,单位:秒
        conOpt.setKeepAliveInterval(3 * 60);
        // 用户名
        conOpt.setUserName(userName);
        // 密码
        conOpt.setPassword(passWord.toCharArray());     //将字符串转换为字符串数组
        //断开后,是否自动连接
        conOpt.setAutomaticReconnect(true);

        // last will message
        boolean doConnect = true;
        String message = "{\"clientId\":\"" + clientId + "\"}";
        Log.e(getClass().getName(), "message is:" + message);
        String topic = "track";
        Integer qos = 1;
        Boolean retained = false;
        if ((!message.equals("")) || (!topic.equals(""))) {
            // 最后的遗嘱
            // MQTT本身就是为信号不稳定的网络设计的,所以难免一些客户端会无故的和Broker断开连接。
            //当客户端连接到Broker时,可以指定LWT,Broker会定期检测客户端是否有异常。
            //当客户端异常掉线时,Broker就往连接时指定的topic里推送当时指定的LWT消息。
            try {
                conOpt.setWill("public", message.getBytes(), 1, retained.booleanValue());
//                conOpt.setWill("e-wallet", message.getBytes(), 2, retained.booleanValue());
            } catch (Exception e) {
                Log.e(TAG, "Exception Occured", e);
                doConnect = false;
                iMqttActionListener.onFailure(null, e);
            }
        }

        if (doConnect) {
            doClientConnection();
        }

    }

 其中userName  passWord和host是与后台商量一致的

前面的工作往后才能后,我们就去调用doClientConnection连接mqtt服务,为了防止重复连接我们添加一个标志位doConnect


    /**
     * 连接MQTT服务器
     */
    private void doClientConnection() {
        if (!client.isConnected()) {
            try {
                client.connect(conOpt, null, iMqttActionListener);
            } catch (MqttException e) {
                Log.e(TAG, e.toString());
                e.printStackTrace();
            }
        } else {
            Log.e(TAG, "mqtt isConnected");
            isConnectSuccess = true;
        }

    }

4,连接后我们就可以 在iMqttActionListener这个回调中判断是否连接成功,然后在连接成功后订阅我们的主题


    // MQTT是否连接成功
    private IMqttActionListener iMqttActionListener = new IMqttActionListener() {

        @Override
        public void onSuccess(IMqttToken arg0) {
            Log.e(TAG, "connect mqtt success");
            isConnectSuccess = true;
            try {
                // 订阅myTopic话题
                int[] qos = {1, 2, 2, 2, 2};//0:最多一次,消息会丢失和重复;1:至少一次,但消息会重复;2:只有一次,确保消息达到一次
                String[] topic = {"track", "e-wallet", "e-QR", "public", "log"};
                client.subscribe(topic, qos);
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(IMqttToken arg0, Throwable arg1) {
            Log.e(TAG, "connect mqtt fail ");
            arg1.printStackTrace();
            // 连接失败,重连
            mqtthandler.removeMessages(0);
            mqtthandler.sendEmptyMessageDelayed(0, 20000);
        }
    };

 由于可能有断网或者网络不正常的情况,所以我写了一个Handler,用来进行连接诶失败后重复连接的操作

 private MQTTHandler mqtthandler = new MQTTHandler(this);

    private static class MQTTHandler extends Handler {
        private WeakReference mqttServiceWeakReference;

        private MQTTHandler(MQTTService mqttService) {
            mqttServiceWeakReference = new WeakReference<>(mqttService);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    if (client != null) {
                        mqttServiceWeakReference.get().doClientConnection();
                    } else {
                        mqttServiceWeakReference.get().init();
                    }
                    break;
            }
        }
    }

 5,然后我们就可以调用publih去发布我们的消息了

        try {
            if (client != null) {
                Log.e("MQTT", "PublishCardData termId:" + CommonUtils.prefixString(jtbCardInfo.getTerm_id(), "0", 18));
                MqttMessage mqttMessage = new MqttMessage();
                mqttMessage.setQos(qos);
                mqttMessage.setRetained(retained);
                mqttMessage.setId(MQTTService.UPLOAD_CARD_MESSAGE_ID);
                mqttMessage.setPayload(builder.build().toByteArray());

                client.publish(myTopic, mqttMessage);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

发布后我们就可以在mqttCallBack回调中收到是否发布成功的信息

  // MQTT监听并且接受消息
    private MqttCallback mqttCallback = new MqttCallback() {
        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            String str2 = "topic:" + topic + "  qos:" + message.getQos() + "   retained:" + message.isRetained();
            Log.e(TAG, "messageArrived:" + str2);
//            if (topic.equals("track")) {
//                byte[] payload = message.getPayload();
//                Track.msg msg = Track.msg.parseFrom(payload);
//                Log.e(TAG, "messageArrived:" + str2 + "  " + msg.toString());
//            }

//            if (topic.equals("e-wallet")) {
//                byte[] payload = message.getPayload();
//                CardOrderModel.CardOrder msg = CardOrderModel.CardOrder.parseFrom(payload);
//                Log.e(TAG, "messageArrived:" + str2 + "  " + msg.toString());
//            }
//            if (topic.equals("e-QR")) {
//                byte[] payload = message.getPayload();
//                QrOrderModel.QrOrder msg = QrOrderModel.QrOrder.parseFrom(payload);
//                Log.e(TAG, "messageArrived:" + str2 + "  " + msg.toString());
//            }
//            if (topic.equals("public")) {
//                byte[] payload = message.getPayload();
//                HeartBeatTempModel.HeartBeatTemp msg = HeartBeatTempModel.HeartBeatTemp.parseFrom(payload);
                Log.e(TAG, "messageArrived:" + str2 + "  " + msg.toString());
//            }


        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken arg0) {
            try {
                MqttMessage mqttMessage = arg0.getMessage();
                int qos = arg0.getMessage().getQos();
                boolean isComplete = arg0.isComplete();
                int messageId = mqttMessage.getId();
                Log.e(TAG, "deliveryComplete:" + isComplete + "  Qos:" + qos + "  messageid:" + messageId);
                if (messageId == UPLOAD_HEART_MESSAGE_ID) {
                    DeviceConfigSpUtil.setMQTTForHeartTime(MQTTService.this, CommonUtils.millToDateToDay(System.currentTimeMillis()));
                }
                if (messageId == UPLOAD_LOG_MESSAGE_ID) {
                    byte[] payload = mqttMessage.getPayload();
                    LogModel.CLog log = LogModel.CLog.parseFrom(payload);
                    j++;
                    if (log.getIsEnd()) {
                        android.util.Log.e(TAG, "文件上传完成:" + log.getFileName());
                        if (curLog >= logFiles.size() - 1) {
                            Log.e(TAG, "所有log上传完成");
                            SpUtils.setMQTTFUploadLogTime(MQTTService.this, CommonUtils.millToDateToDay(System.currentTimeMillis()));

                        } else {
                            curLog++;
                            j = 0;
                            getData(curLog);
                        }

                    }
                    if (j < dataList.size()) {
                        publishLog();
                    }

                }
            } catch (MqttException e) {
                e.printStackTrace();
            } catch (InvalidProtocolBufferException e) {
                e.printStackTrace();
            }


        }

        @Override
        public void connectionLost(Throwable arg0) {
            // 失去连接,重连
            Log.e(TAG, "connectionLost.." + arg0.getMessage() + arg0.toString());
            if (client != null) {
                // 连接失败,重连
                mqtthandler.removeMessages(0);
                mqtthandler.sendEmptyMessageDelayed(0, 20000);
            }
        }
    };

注意: messageArrived回调表示的是服务端发布给我们的数据,如果要接收或者处理,我们可以在这个方法里实现我们的业务逻辑。deliveryComplete表示我们发布给服务器后,是否成功的回调,我们可以在此处理我们的业务逻辑。

我们在发布信息的时候,有可能发布各种不同的的信息,例如各种主题:例如我代码中的主题。那么判断

deliveryComplete中,为了判断是那一个主题发布成功了,所以我发布的时候专门在 MqttMessage中设置了一个ID,

mqttMessage.setId(MQTTService.UPLOAD_CARD_MESSAGE_ID);

这样我就可以通过这个id区判断到底是哪一个主题发布成功了,从而操作我的业务逻辑。

6,重要知识点就是protoBuf协议

由于MQTT传输的时候大多是以protoBuf的格式传输的,所以我们需要交我们的实体类转化为protoBuf的格式:

转化过程如下:

 1,新建一个用来创建protobuf格式数据的工程

 在项目外面那一层的buid.gradle中配置protobuf的依赖:

    dependencies {
        dependencies {
            classpath 'com.android.tools.build:gradle:3.2.1'
            ///**修改**:添加对protobuf插件的依赖
            classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
        }
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

2, 在项目内部的buid.gradle中添加protobuf的依赖

安卓MQTT协议的的使用,并使用protobuf协议传输数据_第1张图片

3.在buid.gradle里添加protobuf的插件配置,与上面的dependencies平级

///**修改2**:增加protobuf插件编译配置
protobuf {
    protoc {
        artifact = 'com.google.protobuf:protoc:3.0.0'
    }
    plugins {
        javalite {
            artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
        }
    }
    generateProtoTasks {
        all().each { task ->
            task.plugins {
                javalite {}
            }
        }
    }
}

 4,在buid.gradle里定义proto的文件目录

   sourceSets {
        main {
            // 定义proto文件目录
            proto {
                srcDir 'src/main/proto'
            }
        }
    }

这个目录是根据自己项目包的构造二命名的

安卓MQTT协议的的使用,并使用protobuf协议传输数据_第2张图片

5,在这个目录里放入proto格式的文件

例如:

我的track.proto文件里是这样写的,其中的com.bsit.qm702.mqtt是我正式项目中,要放置生成proto文件的目录,如果你想放置到其他目录可进行相应的修改

syntax = "proto3";
option java_package = "com.bsit.qm702.mqtt";
option java_outer_classname = "Track";
// 公交车轨迹数据
message msg {

    string Termid = 1;
    string Posid = 2;
    string Samid = 3;
    string INSTACP = 4;
    string longitude = 5;
    string latitude = 6;
    string BusLine = 7;
    string speed = 8;
    string Plate = 9;
    string check = 10;
    string Time = 11;
    string Direction = 12;
    string BusDirection = 13;
    string Elevation = 14;

}

 然后点击项目的make进行编译,编译通过后会在指定目录生成对应的proto文件的java格式的文件

安卓MQTT协议的的使用,并使用protobuf协议传输数据_第3张图片

然后我们就可以把这个文件copy到我们的项目里使用,主要包名要对应住。

使用的时候我们就可以调用如下方法往这个Track对象里赋值

Track.msg.Builder builder = Track.msg.newBuilder();
builder.setTermid(CommonUtils.prefixString(psamTermNo, "0", 18));

builder.setSamid(CommonUtils.prefixString(sam, "0", 20));

builder.setPosid(CommonUtils.prefixString(psamTermNo, "0", 12));

 只要调用buider对应的set方法就可以赋值,其他对象也是一样的套路。这样整个MQTT协议传送数据的过程就完成了

你可能感兴趣的:(Android,开发,android)