You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Android无法建立MQTT连接:空指针异常求助

排查Paho MQTT Android客户端的NullPointerException问题

我来帮你捋一捋这个问题哈,从错误栈来看,问题出在Paho客户端的AlarmPingSender.stop()方法里——它调用AlarmManager.cancel(null)的时候触发了空指针异常,说明用来取消闹钟的PendingIntent是null值。

咱们先拆解下错误的核心:

java.lang.NullPointerException: cancel() called with a null PendingIntent at android.app.AlarmManager.cancel(AlarmManager.java:901) at org.eclipse.paho.android.service.AlarmPingSender.stop(AlarmPingSender.java:86)

这个AlarmPingSender是Paho客户端用来定期发送MQTT心跳包的组件,它会用AlarmManager设置定时任务,当客户端断开连接时,会调用stop()方法取消这个定时任务。如果这个时候PendingIntent还没被初始化(或者已经被释放了),就会出现这个空指针。

可能的触发原因

  • 客户端还未完成连接就被断开:比如用户在连接请求还没成功返回时,就点击了断开按钮,导致AlarmPingSenderstart()方法还没执行(还没创建PendingIntent),就调用了stop()
  • 旧版本Paho客户端的bug:早期版本的AlarmPingSender没有做null判断,直接调用AlarmManager.cancel(),而部分Android版本对null参数的处理不够兼容。

解决方案

1. 确保断开操作只在连接成功后执行

给你的代码加一个连接状态标记,避免在未连接时调用断开:

// 定义一个全局状态变量
private boolean isMqttConnected = false;

// 在连接成功的回调里更新状态
@Override
public void onSuccess(IMqttToken asyncActionToken) {
    isMqttConnected = true;
    // 你的其他UI更新逻辑...
}

// 断开连接的方法里增加判断
public void disconnectMqtt() {
    if (client != null && isMqttConnected) {
        try {
            client.disconnect();
            isMqttConnected = false;
            // 重置UI状态...
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }
}

2. 自定义安全的AlarmPingSender

如果默认的组件有问题,咱们自己实现一个带null判断的PingSender,替换掉默认的:

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.internal.ClientComms;

public class SafeAlarmPingSender implements PingSender {
    private AlarmManager alarmManager;
    private PendingIntent pendingIntent;
    private MqttAndroidClient client;

    @Override
    public void init(MqttAndroidClient client) {
        this.client = client;
        alarmManager = (AlarmManager) client.getContext().getSystemService(Context.ALARM_SERVICE);
    }

    @Override
    public void start() {
        // 初始化PendingIntent再设置闹钟
        Intent intent = new Intent(client.getContext(), PingReceiver.class);
        intent.setAction("org.eclipse.paho.android.service.mqttping");
        // 根据Android版本选择合适的Flag
        int flags = PendingIntent.FLAG_UPDATE_CURRENT;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            flags |= PendingIntent.FLAG_IMMUTABLE;
        }
        pendingIntent = PendingIntent.getBroadcast(client.getContext(), 0, intent, flags);

        // 计算心跳间隔,取保活时间的一半
        long keepAliveInterval = client.getKeepAlive() * 1000;
        long delay = keepAliveInterval / 2;
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delay, pendingIntent);
    }

    @Override
    public void stop() {
        // 先判断null再执行取消操作
        if (alarmManager != null && pendingIntent != null) {
            alarmManager.cancel(pendingIntent);
            pendingIntent.cancel();
        }
    }

    // 对应的心跳广播接收器
    public static class PingReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String clientHandle = intent.getStringExtra("clientHandle");
            MqttAndroidClient client = MqttAndroidClient.getInstance(context, clientHandle);
            if (client != null) {
                try {
                    ClientComms clientComms = client.getClient().getComms();
                    if (clientComms != null && clientComms.isConnected()) {
                        clientComms.sendPingReq();
                    }
                } catch (MqttException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

然后在创建MqttAndroidClient后,设置这个自定义的PingSender:

client = new MqttAndroidClient(getApplicationContext(), "ssl://192.168.43.112:1883", clientId);
// 设置自定义的PingSender
client.setPingSender(new SafeAlarmPingSender());
// 后续的连接逻辑不变...

3. 升级Paho客户端版本

这个问题在Paho的新版本里已经被修复了,你可以尝试升级依赖包的版本。比如在build.gradle(Module级别)里更新:

dependencies {
    // 升级到最新稳定版
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.4'
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
}

你可以先试试升级版本,如果还是不行,再尝试加状态判断或者自定义PingSender的方案。

内容的提问来源于stack exchange,提问作者Hitesh Pratyush V

火山引擎 最新活动