Android --- MQTT

目录

MQTT介绍

MQTT协议中的订阅、主题、会话

一、订阅(Subscription)

二、会话(Session)

三、主题名(Topic Name)

四、主题筛选器(Topic Filter)

五、负载(Payload)

MQTT协议中的方法

MQTT服务质量 (QoS)

Android 具体实现

第一步,添加依赖

第二步,声明权限

第三步,开启服务:

第四步,具体实现

创建Service

回调接口:

实现一个ServiceConnection类

使用:


MQTT介绍

 实现MQTT协议需要客户端和服务器端通讯完成。

在通讯过程中,MQTT协议中有三种身份:

发布者(Publish)

代理(Broker)(服务器)

订阅者(Subscribe)

消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。

MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:

        Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内(payload);
        payload,可以理解为消息的内容,是指订阅者具体要使用的内容。


MQTT会构建底层网络传输:它将建立客户端到服务器的连接,提供两者之间的一个有序的、无损的、基于字节流的双向传输。
当应用数据通过MQTT网络发送时,MQTT会把与之相关的服务质量(QoS)和主题名(Topic)相关连。

MQTT协议中的订阅、主题、会话

一、订阅(Subscription)

            订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。

二、会话(Session)


  每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。


三、主题名(Topic Name)


  连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。


四、主题筛选器(Topic Filter)


  一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。


五、负载(Payload)


  消息订阅者所具体接收的内容。
 

MQTT协议中的方法

  • Connect。等待与服务器建立连接。
  • Disconnect。等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。
  • Subscribe。等待完成订阅。
  • UnSubscribe。等待服务器取消客户端的一个或多个topics订阅。
  • Publish。MQTT客户端发送消息请求,发送完成后返回应用程序线程。

MQTT服务质量 (QoS)

        qos为0:“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
        qos为1:“至少一次”,确保消息到达,但消息重复可能会发生。这一级别可用于如下情况,你需要获得每一条消息,并且消息重复发送对你的使用场景无影响。
        qos为2:“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。

Android 具体实现

首先先要确认配置参数:

  • Topic:订阅的事件。也就是“主题”——ForTest。
  • URI:MQTT服务器的地址。
  • username & password:账户与密码。
  • ClientId:客户端的ID,可以自定义,必须保证唯一性,否则连接服务器的时候会导致服务器断开,不要问我是怎么知道的。

然后,配置android中的mqtt相关库。

第一步,添加依赖

在项目根目录下的build.gradle中添加:

repositories {
    maven {
        url "https://repo.eclipse.org/content/repositories/paho-releases/"
    }
}

 然后在app目录下的build.gradle中添加:

dependencies {
    compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
    compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.1'
}
第二步,声明权限

在AndroidManifest.xml中添加:

    
    
    
    
    
    
第三步,开启服务:

同样是在AndroidManifest.xml中添加:

    
    

配置结束。

=====================================================================

第四步,具体实现

考虑一下项目中,mqtt作用是什么,其实就是通信、建立长连接。那么肯定是要放在服务里面的

MQTT 的配置放入Service中去,所以需要创建一个Service。

创建Service
class MQTTService : Service() {
    private var conOpt: MqttConnectOptions? = null
    private val host = "tcp://192.168.0.11:61613"
    private val userName = "admin"
    private val passWord = "password"
    private val clientId = "androidId" //客户端标识
    private var IGetMessageCallBack: IGetMessageCallBack? = null
    override fun onCreate() {
        super.onCreate()
        Log.e(javaClass.name, "onCreate")
        init()
    }

    private fun init() {
        // 服务器地址(协议+地址+端口号)
        val uri = host
        MQTTService.Companion.client = MqttAndroidClient(this, uri, clientId)
        // 设置MQTT监听并且接受消息
        MQTTService.Companion.client.setCallback(mqttCallback)
        conOpt = MqttConnectOptions()
        // 清除缓存
        conOpt.setCleanSession(true)
        // 设置超时时间,单位:秒
        conOpt.setConnectionTimeout(10)
        // 心跳包发送间隔,单位:秒
        conOpt.setKeepAliveInterval(20)
        // 用户名
        conOpt.setUserName(userName)
        // 密码
        conOpt.setPassword(passWord.toCharArray()) //将字符串转换为字符串数组

        // last will message
        var doConnect = true
        val message = "{\"terminal_uid\":\"$clientId\"}"
        Log.e(javaClass.name, "message是:$message")
        val topic: String = MQTTService.Companion.myTopic
        val qos = 0
        val retained = false
        if (message != "" || topic != "") {
            // 最后的遗嘱
            // MQTT本身就是为信号不稳定的网络设计的,所以难免一些客户端会无故的和Broker断开连接。
            //当客户端连接到Broker时,可以指定LWT,Broker会定期检测客户端是否有异常。
            //当客户端异常掉线时,Broker就往连接时指定的topic里推送当时指定的LWT消息。
            try {
                conOpt.setWill(topic, message.toByteArray(), qos, retained.toBoolean())
            } catch (e: Exception) {
                Log.i(MQTTService.Companion.TAG, "Exception Occured", e)
                doConnect = false
                iMqttActionListener.onFailure(null, e)
            }
        }
        if (doConnect) {
            doClientConnection()
        }
    }

    override fun onDestroy() {
        stopSelf()
        try {
            MQTTService.Companion.client.disconnect()
        } catch (e: MqttException) {
            e.printStackTrace()
        }
        super.onDestroy()
    }

    /** 连接MQTT服务器  */
    private fun doClientConnection() {
        if (!MQTTService.Companion.client.isConnected() && isConnectIsNormal) {
            try {
                MQTTService.Companion.client.connect(conOpt, null, iMqttActionListener)
            } catch (e: MqttException) {
                e.printStackTrace()
            }
        }
    }

    // MQTT是否连接成功
    private val iMqttActionListener: IMqttActionListener = object : IMqttActionListener() {
        fun onSuccess(arg0: IMqttToken?) {
            Log.i(MQTTService.Companion.TAG, "连接成功 ")
            try {
                // 订阅myTopic话题
                MQTTService.Companion.client.subscribe(MQTTService.Companion.myTopic, 1)
            } catch (e: MqttException) {
                e.printStackTrace()
            }
        }

        fun onFailure(arg0: IMqttToken?, arg1: Throwable) {
            arg1.printStackTrace()
            // 连接失败,重连
        }
    }

    // MQTT监听并且接受消息
    private val mqttCallback: MqttCallback = object : MqttCallback() {
        @Throws(Exception::class)
        fun messageArrived(topic: String, message: MqttMessage) {
            val str1: String = String(message.getPayload())
            if (IGetMessageCallBack != null) {
                IGetMessageCallBack.setMessage(str1)
            }
            val str2 = topic + ";qos:" + message.getQos() + ";retained:" + message.isRetained()
            Log.i(MQTTService.Companion.TAG, "messageArrived:$str1")
            Log.i(MQTTService.Companion.TAG, str2)
        }

        fun deliveryComplete(arg0: IMqttDeliveryToken?) {}
        fun connectionLost(arg0: Throwable?) {
            // 失去连接,重连
        }
    }

    /** 判断网络是否连接  */
    private val isConnectIsNormal: Boolean
        private get() {
            val connectivityManager: ConnectivityManager = this.applicationContext
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val info: NetworkInfo = connectivityManager.getActiveNetworkInfo()
            return if (info != null && info.isAvailable()) {
                val name: String = info.getTypeName()
                Log.i(MQTTService.Companion.TAG, "MQTT当前网络名称:$name")
                true
            } else {
                Log.i(MQTTService.Companion.TAG, "MQTT 没有可用网络")
                false
            }
        }

    override fun onBind(intent: Intent?): IBinder {
        Log.e(javaClass.name, "onBind")
        return CustomBinder()
    }

    fun setIGetMessageCallBack(IGetMessageCallBack: IGetMessageCallBack?) {
        this.IGetMessageCallBack = IGetMessageCallBack
    }

    inner class CustomBinder : Binder() {
        val service: MQTTService
            get() = this@MQTTService
    }

    fun toCreateNotification(message: String?) {
        val pendingIntent: PendingIntent = PendingIntent.getActivity(
            this, 1, Intent(
                this,
                MQTTService::class.java
            ), PendingIntent.FLAG_UPDATE_CURRENT
        )
        val builder: NotificationCompat.Builder = Builder(this) //3、创建一个通知,属性太多,使用构造器模式
        val notification: Notification = builder
            .setTicker("测试标题")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle("")
            .setContentText(message)
            .setContentInfo("")
            .setContentIntent(pendingIntent) //点击后才触发的意图,“挂起的”意图
            .setAutoCancel(true) //设置点击之后notification消失
            .build()
        val notificationManager: NotificationManager =
            getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        startForeground(0, notification)
        notificationManager.notify(0, notification)
    }

    companion object {
        val TAG = MQTTService::class.java.simpleName
        private val client: MqttAndroidClient? = null
        private const val myTopic = "ForTest" //要订阅的主题
        fun publish(msg: String) {
            val topic: String = MQTTService.Companion.myTopic
            val qos = 0
            val retained = false
            try {
                if (MQTTService.Companion.client != null) {
                    MQTTService.Companion.client.publish(
                        topic,
                        msg.toByteArray(),
                        qos,
                        retained.toBoolean()
                    )
                }
            } catch (e: MqttException) {
                e.printStackTrace()
            }
        }
    }
}
回调接口:
interface IGetMessageCallBack {
    void setMessage(message:String )
}

看过的博客中都是通过startService去启动服务的,那样搞的话有点耦合。不太好。

这里是通过BindService去启动服务,所以没有onStartCommond方法。

同时,当获取从服务器推送过来的消息时,是使用回调去更新UI,这样做是为了方便代码的迁移。

关于 bindservice和startservice 区别 可以去看看Android Service 使用bindservice和startservice 区别

实现一个ServiceConnection类

为了实现通过这个回调去传递从服务端获取到的消息,我们需要实现一个ServiceConnection类,并且通过onBind来从Service和Activity之间传递数据:

class MyServiceConnection : ServiceConnection {
    var mqttService: MQTTService? = null
        private set
    private var IGetMessageCallBack: IGetMessageCallBack? = null
    override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
        mqttService = (iBinder as MQTTService.CustomBinder).getService()
        mqttService!!.setIGetMessageCallBack(IGetMessageCallBack)
    }

    override fun onServiceDisconnected(componentName: ComponentName) {}
    fun setIGetMessageCallBack(IGetMessageCallBack: IGetMessageCallBack?) {
        this.IGetMessageCallBack = IGetMessageCallBack
    }
}
使用:

class MainActivity : AppCompatActivity(), IGetMessageCallBack {
    private var textView: TextView? = null
    private var button: Button? = null
    private var serviceConnection: MyServiceConnection? = null
    private var mqttService: MQTTService? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView = findViewById(R.id.text) as TextView
        button = findViewById(R.id.button) as Button
        serviceConnection = MyServiceConnection()
        serviceConnection!!.setIGetMessageCallBack(this@MainActivity)
        val intent = Intent(this, MQTTService::class.java)
        bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
        button.setOnClickListener(object : OnClickListener() {
            fun onClick(view: View?) {
                MQTTService.publish("测试一下子")
            }
        })
    }

    fun setMessage(message: String?) {
        textView!!.text = message
        mqttService = serviceConnection!!.mqttService
        mqttService!!.toCreateNotification(message)
    }

    override fun onDestroy() {
        unbindService(serviceConnection!!)
        super.onDestroy()
    }
}

结束。

你可能感兴趣的:(网络,android)