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

Android:主Activity与自定义传感器类的蓝牙回调通信问题

问题解答:BluetoothGattCallback触发时调用主Activity方法的实现方式

当然可以实现!不过得注意Android的线程模型和内存泄漏问题,下面给你几种靠谱的实现方案,适配不同的开发场景:


1. 接口回调(最推荐,解耦性强)

这是Android开发中最常用的解耦方式,通过定义回调接口,让传感器类和主Activity之间通过接口通信,避免直接持有Activity引用。

步骤1:定义回调接口

public interface GattCallbackListener {
    void onGattDeviceConnected(String macAddress);
    void onGattDataReceived(String macAddress, byte[] data);
    // 可根据需求扩展更多回调方法,比如连接断开、特征值写入结果等
}

步骤2:在自定义传感器类中持有接口引用

public class CustomSensor {
    private String macAddress;
    private String deviceName;
    private BluetoothGattCallback gattCallback;
    private GattCallbackListener listener;

    // 提供设置监听器的方法
    public void setGattCallbackListener(GattCallbackListener listener) {
        this.listener = listener;
    }

    private void initGattCallback() {
        gattCallback = new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
                // 触发回调前先判空,避免空指针
                if (newState == BluetoothProfile.STATE_CONNECTED && listener != null) {
                    listener.onGattDeviceConnected(macAddress);
                }
            }

            @Override
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicRead(gatt, characteristic, status);
                if (status == BluetoothGatt.GATT_SUCCESS && listener != null) {
                    listener.onGattDataReceived(macAddress, characteristic.getValue());
                }
            }
        };
    }
}

步骤3:主Activity实现接口并绑定监听器

public class MainActivity extends AppCompatActivity implements GattCallbackListener {
    private ArrayList<CustomSensor> sensorList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 创建传感器实例并绑定监听器
        CustomSensor sensor = new CustomSensor();
        sensor.setGattCallbackListener(this);
        sensorList.add(sensor);
    }

    @Override
    public void onGattDeviceConnected(String macAddress) {
        // 注意:BluetoothGattCallback的方法运行在后台线程,更新UI必须切换到主线程
        runOnUiThread(() -> {
            Toast.makeText(this, "设备 " + macAddress + " 已连接", Toast.LENGTH_SHORT).show();
            // 这里可以做UI更新、数据记录等操作
        });
    }

    @Override
    public void onGattDataReceived(String macAddress, byte[] data) {
        runOnUiThread(() -> {
            // 处理收到的蓝牙设备数据,比如解析后显示在列表中
        });
    }
}

2. 弱引用(适合简单场景,避免内存泄漏)

如果不想用接口,也可以让传感器类持有主Activity的弱引用,这样不会因为传感器实例长期存在导致Activity内存泄漏。

public class CustomSensor {
    private WeakReference<MainActivity> activityWeakRef;
    private BluetoothGattCallback gattCallback;

    public CustomSensor(MainActivity activity) {
        this.activityWeakRef = new WeakReference<>(activity);
        initGattCallback();
    }

    private void initGattCallback() {
        gattCallback = new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
                MainActivity activity = activityWeakRef.get();
                // 检查Activity是否存活,避免操作已销毁的界面
                if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
                    activity.onGattDeviceConnected(macAddress);
                }
            }
        };
    }
}

然后在主Activity中定义对应的处理方法:

public void onGattDeviceConnected(String macAddress) {
    runOnUiThread(() -> {
        // 处理连接逻辑
    });
}

3. LiveData(适配MVVM架构)

如果你的项目采用MVVM架构,用LiveData传递状态和数据是更优雅的方式,LiveData会自动处理线程切换和生命周期感知,避免内存泄漏。

传感器类中定义LiveData

public class CustomSensor {
    private MutableLiveData<SensorConnectionState> connectionStateLiveData = new MutableLiveData<>();
    private MutableLiveData<SensorData> dataLiveData = new MutableLiveData<>();

    private void initGattCallback() {
        gattCallback = new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
                // 用postValue在后台线程更新LiveData
                connectionStateLiveData.postValue(new SensorConnectionState(macAddress, newState));
            }

            @Override
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicRead(gatt, characteristic, status);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    dataLiveData.postValue(new SensorData(macAddress, characteristic.getValue()));
                }
            }
        };
    }

    // 对外提供不可变的LiveData
    public LiveData<SensorConnectionState> getConnectionStateLiveData() {
        return connectionStateLiveData;
    }

    public LiveData<SensorData> getDataLiveData() {
        return dataLiveData;
    }

    // 定义数据实体类
    public static class SensorConnectionState {
        public String macAddress;
        public int state;
        // 构造方法省略
    }

    public static class SensorData {
        public String macAddress;
        public byte[] data;
        // 构造方法省略
    }
}

主Activity中观察LiveData

sensor.getConnectionStateLiveData().observe(this, state -> {
    // LiveData自动切换到主线程,无需手动调用runOnUiThread
    Toast.makeText(this, "设备 " + state.macAddress + " 状态更新:" + state.state, Toast.LENGTH_SHORT).show();
});

关键注意事项

  • 内存泄漏防护:无论用哪种方式,在Activity销毁时(比如onDestroy()方法中),都要遍历sensorList,调用bluetoothGatt.close()释放蓝牙连接,并清空监听器或弱引用。
  • 线程切换:BluetoothGatt的所有回调方法都运行在后台线程,直接操作UI会导致崩溃,必须切换到主线程处理UI更新。
  • 多设备区分:每个CustomSensor实例要绑定唯一的MAC地址,这样回调时能准确识别是哪台设备触发的事件。

内容的提问来源于stack exchange,提问作者C.Cordier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:04:52