You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何拆分蓝牙连接与消息发送方法?解决空指针异常

拆分蓝牙连接与消息发送方法的解决方案

完全可以将蓝牙连接和消息发送拆分为两个独立方法,你遇到的空指针错误(Attempt to invoke virtual method 'void java.io.OutputStream.write(byte[])' on a null object reference),本质是拆分后没有正确保存连接后的BluetoothSocket、OutputStream等核心对象,导致发送消息时这些对象为null。

下面给出具体的实现方案:

核心思路

  • 用类的实例变量保存连接后的BluetoothSocket、InputStream、OutputStream,确保跨方法访问时对象有效
  • 连接方法仅负责建立连接并初始化上述实例变量
  • 发送方法复用已保存的实例变量完成消息发送,发送前先校验连接状态
  • 蓝牙操作必须在后台线程执行(避免阻塞主线程),React Native的@ReactMethod默认在主线程,所以要手动切换线程
  • 提供连接关闭方法,及时释放资源并重置实例变量

具体代码实现

1. 定义实例变量

在你的蓝牙模块类中添加以下实例变量:

private BluetoothSocket bluetoothSocket;
private InputStream inputStream;
private OutputStream outputStream;
// 通用SPP服务UUID,大部分蓝牙设备支持
private final UUID BLUETOOTH_SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

2. 连接设备方法

@ReactMethod
public void connectToDevice(String deviceAddress, Promise promise) {
    // 切换到后台线程执行蓝牙连接操作
    new Thread(() -> {
        try {
            // 先关闭已有连接,避免重复连接
            if (bluetoothSocket != null && bluetoothSocket.isConnected()) {
                closeConnectionInternal();
            }

            BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
            bluetoothSocket = device.createRfcommSocketToServiceRecord(BLUETOOTH_SPP_UUID);
            bluetoothSocket.connect();
            
            // 初始化输入输出流
            inputStream = bluetoothSocket.getInputStream();
            outputStream = bluetoothSocket.getOutputStream();

            // 回到主线程resolve Promise(React Native要求Promise操作在主线程)
            PromiseUtils.resolvePromiseOnMainThread(promise, "设备连接成功");
        } catch (IOException e) {
            e.printStackTrace();
            PromiseUtils.rejectPromiseOnMainThread(promise, new Throwable("连接失败: " + e.getMessage()));
        }
    }).start();
}

3. 发送消息方法

@ReactMethod
public void sendMessage(String message, Promise promise) {
    new Thread(() -> {
        try {
            // 校验连接状态,避免空指针
            if (bluetoothSocket == null || !bluetoothSocket.isConnected() || outputStream == null) {
                throw new IOException("未建立有效蓝牙连接,请先调用connectToDevice");
            }

            // 发送消息
            outputStream.write(message.getBytes());
            outputStream.flush();

            // 读取设备响应
            byte[] buffer = new byte[1024];
            int bytesRead = inputStream.read(buffer);
            String response = new String(buffer, 0, bytesRead);

            PromiseUtils.resolvePromiseOnMainThread(promise, response);
        } catch (IOException e) {
            e.printStackTrace();
            PromiseUtils.rejectPromiseOnMainThread(promise, new Throwable("发送失败: " + e.getMessage()));
        }
    }).start();
}

4. 关闭连接方法

@ReactMethod
public void closeConnection(Promise promise) {
    new Thread(() -> {
        try {
            closeConnectionInternal();
            PromiseUtils.resolvePromiseOnMainThread(promise, "连接已关闭");
        } catch (IOException e) {
            e.printStackTrace();
            PromiseUtils.rejectPromiseOnMainThread(promise, new Throwable("关闭连接失败: " + e.getMessage()));
        }
    }).start();
}

// 内部工具方法,统一处理资源释放
private void closeConnectionInternal() throws IOException {
    if (outputStream != null) {
        outputStream.close();
        outputStream = null;
    }
    if (inputStream != null) {
        inputStream.close();
        inputStream = null;
    }
    if (bluetoothSocket != null) {
        bluetoothSocket.close();
        bluetoothSocket = null;
    }
}

5. Promise主线程处理工具

需要一个工具类来确保Promise在主线程完成(React Native的Promise必须在调用它的线程(主线程)resolve/reject):

import com.facebook.react.bridge.Promise;
import android.os.Handler;
import android.os.Looper;

public class PromiseUtils {
    public static void resolvePromiseOnMainThread(Promise promise, Object result) {
        new Handler(Looper.getMainLooper()).post(() -> promise.resolve(result));
    }

    public static void rejectPromiseOnMainThread(Promise promise, Throwable error) {
        new Handler(Looper.getMainLooper()).post(() -> promise.reject(error));
    }
}

关键注意事项

  • 线程切换:所有蓝牙相关操作(连接、发送、关闭)都必须在后台线程执行,否则会触发ANR(应用无响应)
  • 状态校验:发送消息前一定要检查连接状态,避免因连接断开、未初始化导致的空指针
  • 资源释放:不再使用连接时务必调用closeConnection释放资源,避免内存泄漏
  • UUID兼容性:示例中使用的是通用SPP服务UUID,若你的设备使用自定义UUID,需要替换为对应值

内容的提问来源于stack exchange,提问作者new learner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 05:34:53