Android消息推送MQTT 填坑

Android 集成 mqtt 开启关闭网络 应用崩溃 

一.开发环境

Android studio kotlin  

前提是你需要有一个MQTT代理服务器可以连接,这里我也写了一篇说明的文档,以供参考,链接如下:

https://blog.csdn.net/wwanglin_0/article/details/110529367

官方demo

eclipse/paho.mqtt.android: MQTT Android (github.com)

git clone https://github.com/eclipse/paho.mqtt.android.git

注:git clone 后切换到develop版本 ,master版本在最新 Android studio中不能运行

我做的demo 请参考另一篇博文 https://blog.csdn.net/wwanglin_0/article/details/110549899

 

以下是我的app版本,目的是让你看到我的版本有多高,不是让你改成我的一样

compileSdkVersion 30
buildToolsVersion "30.0.2"
minSdkVersion 21
targetSdkVersion 30

二.集成步骤

1.依赖

//MQTT
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
//解决Android开发中LocalBroadcastManager类无法使用的问题
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

2.权限

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

3.服务

<!--MqttService-->
<service android:name="org.eclipse.paho.android.service.MqttService" />

4.将mqtt集成在MyApplication中

使用代码写在MyApplication能保证程序一直活着,随时能接收订阅的消息,另外程序中设置了断开重连机制


class MyApplication : Application() {
    
    /*全局单例 */
    companion object {

        private const val TAG = "MyApplication"

        //全局上下文
        lateinit var context: Context

        // 单例不会是null   所以使用notNull委托
        var instance: MyApplication by Delegates.notNull()


        //MQTT
        const val SUBSCRIPTION_TOPIC = "SubscriptionTopic"
        const val PUBLISH_TOPIC = "PublishTopic"
        const val baseURL: String="192.168.1.141"
        const val MQTT_SERVER_USERNAME = "admin"
        const val MQTT_SERVER_PASSWORD = "admin"
        lateinit var mqttAndroidClient: MqttAndroidClient
        lateinit var mqttUrl: String
        var clientId = "roomAndroidClient"


    }



    override fun onCreate() {
        super.onCreate()

        instance = this

        /*全局上下文*/
        context = applicationContext

        initMQTT()
    }

    private fun initMQTT() {

        mqttUrl = "tcp://192.168.0.141:1883"
        //创建mqtt连接
        clientId += System.currentTimeMillis()
        mqttAndroidClient = MqttAndroidClient(applicationContext, mqttUrl, clientId)
        //设置mqtt连接状态回调
        mqttAndroidClient.setCallback(object : MqttCallbackExtended  {
            override fun connectComplete(reconnect: Boolean, serverURI: String) {
                if (reconnect) {
                    Log.d("mqtt", "Reconnected to : $serverURI")
                    // Because Clean Session is true, we need to re-subscribe
                    subscribeToTopic()
                } else {
                    Log.d("mqtt", ("Connected to: $serverURI"))
                }
            }

            override fun connectionLost(cause: Throwable) {
                Log.d("mqtt", "The Connection was lost.")
            }

            @Throws(java.lang.Exception::class)
            override fun messageArrived(topic: String, message: MqttMessage) {
                Log.d("mqtt", "Incoming message: " + String(message.payload))
            }

            override fun deliveryComplete(token: IMqttDeliveryToken) {}
        } )


        //设置mqtt连接选项(自动重连)
        val mqttConnectOptions = MqttConnectOptions()
        mqttConnectOptions.isAutomaticReconnect = true
        mqttConnectOptions.isCleanSession = false
        mqttConnectOptions.userName = MQTT_SERVER_USERNAME
        mqttConnectOptions.password = MQTT_SERVER_PASSWORD.toCharArray()

        //连接mqtt
        Log.d("mqtt", "Connecting to $mqttUrl");

        mqttAndroidClient.connect(mqttConnectOptions, null, object : IMqttActionListener {
            override fun onSuccess(asyncActionToken: IMqttToken) {
                //断开连接的缓冲选项
                val disconnectedBufferOptions = DisconnectedBufferOptions()
                disconnectedBufferOptions.isBufferEnabled = true
                disconnectedBufferOptions.bufferSize = 100
                disconnectedBufferOptions.isPersistBuffer = false
                disconnectedBufferOptions.isDeleteOldestMessages = false
                mqttAndroidClient.setBufferOpts(disconnectedBufferOptions)

                //订阅主题
                subscribeToTopic()
            }

            override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                Log.d("mqtt", "Failed to connect to: $mqttUrl")
            }
        })
    }

    //订阅
    fun subscribeToTopic() {
        //订阅动作回调
        mqttAndroidClient.subscribe(SUBSCRIPTION_TOPIC, 0, null, object : IMqttActionListener {
            override fun onSuccess(asyncActionToken: IMqttToken) {
                Log.d("mqtt", "Subscribed!")
            }

            override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                Log.d("mqtt", "Failed to subscribe")

            }
        })
    }

    //发布
    fun publishMessage(publishMessage: String) {
        val message = MqttMessage()
        message.payload = publishMessage.toByteArray()
        mqttAndroidClient.publish(PUBLISH_TOPIC, message)
        Log.d("mqtt", "Message Published")
        if (!mqttAndroidClient.isConnected) {
            Log.d(
                "mqtt",
                mqttAndroidClient.bufferedMessageCount.toString() + " messages in buffer."
            )
        }
    }





}






5.别忘记将MyApplication添加到清单文件中

<application
        android:name=".MyApplication"
       ...>
       ...

    </application>

三.运行,报错,解决办法

1.报错情景描述:关闭网络,app启动,启动网络,崩溃,崩溃信息如下

2020-12-03 11:04:58.554 13943-13943/com.rfzn.dirt_room E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.rfzn.dirt_room, PID: 13943
    java.lang.RuntimeException: Error receiving broadcast Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x4000010 (has extras) } in org.eclipse.paho.android.service.MqttService$NetworkConnectionIntentReceiver@fd171f4
        at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:1132)
        at android.os.Handler.handleCallback(Handler.java:755)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6141)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void java.util.Timer.cancel()' on a null object reference
        at org.eclipse.paho.client.mqttv3.MqttAsyncClient.stopReconnectCycle(MqttAsyncClient.java:1120)
        at org.eclipse.paho.client.mqttv3.MqttAsyncClient.reconnect(MqttAsyncClient.java:1057)
        at org.eclipse.paho.android.service.MqttConnection.reconnect(MqttConnection.java:1049)
        at org.eclipse.paho.android.service.MqttService.reconnect(MqttService.java:342)
        at org.eclipse.paho.android.service.MqttService$NetworkConnectionIntentReceiver.onReceive(MqttService.java:827)
        at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:1122)
        at android.os.Handler.handleCallback(Handler.java:755) 
        at android.os.Handler.dispatchMessage(Handler.java:95) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6141) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802) 

2.解决报错

将mqttv3版本升级成1.2.0, clean Project

    //MQTT
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0'
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
    //解决Android开发中LocalBroadcastManager类无法使用的问题
    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值