如何监听多个DBus信号以同步蓝牙已连接设备数量
如何监听多个DBus信号以同步蓝牙已连接设备数量
我之前在做GTK蓝牙工具的时候也卡在这里过——要同步已连接设备数量,总不能给每个设备都单独写监听,还得兼顾GTK的单线程模型,确实有点头疼。不过后来摸索出了一套用BlueZ ObjectManager动态监听的方法,刚好能解决你的问题:
核心思路:用ObjectManager跟踪设备生命周期,动态注册信号监听
BlueZ的ObjectManager本身就负责管理所有蓝牙设备的接口生命周期,我们可以利用它的信号来动态维护设备列表,同时给每个设备的org.bluez.Device1接口注册/注销PropertiesChanged监听,完全不用提前知道设备数量。
具体步骤
获取初始设备列表并注册基础监听
首先通过ObjectManager拿到当前所有已知设备,把每个设备的路径和对应的org.bluez.Device1接口存起来(比如用一个字典),同时给每个设备注册PropertiesChanged信号的回调。监听设备的添加与移除
- 注册ObjectManager的
InterfacesAdded信号:当有新设备被发现时,检查是否包含org.bluez.Device1接口,如果是,就把它加入你的设备列表,同时注册它的PropertiesChanged回调。 - 注册ObjectManager的
InterfacesRemoved信号:当设备被移除时,从列表中删除它,同时注销对应的PropertiesChanged回调(避免内存泄漏)。
- 注册ObjectManager的
处理设备属性变化
对于每个设备的PropertiesChanged信号回调,只需要关注Connected属性的变化——当这个属性从False变True时,已连接计数加1;从True变False时,计数减1,直接在回调里更新你的UI或者计数变量就行。
举个Python+GLib/Gio的简单示例
from gi.repository import Gio, GLib, Gtk # 存储设备:键为设备路径,值为Device1接口代理 devices = {} connected_count = 0 def update_connected_count(is_connected): global connected_count if is_connected: connected_count += 1 else: connected_count -= 1 # 这里可以更新GTK UI,比如标签文本 print(f"当前已连接设备数:{connected_count}") def on_device_properties_changed(proxy, changed_props, invalidated_props): # 只关心Connected属性变化 if "Connected" in changed_props: update_connected_count(changed_props["Connected"]) def on_interfaces_added(object_manager, object_path, interfaces): if "org.bluez.Device1" in interfaces: # 获取设备的Device1接口代理 device_proxy = object_manager.get_object(object_path).get_interface("org.bluez.Device1") devices[object_path] = device_proxy # 注册PropertiesChanged回调 device_proxy.connect("g-properties-changed", on_device_properties_changed) # 检查初始连接状态 if device_proxy.get_cached_property("Connected"): update_connected_count(True) def on_interfaces_removed(object_manager, object_path, interfaces): if "org.bluez.Device1" in interfaces and object_path in devices: device_proxy = devices.pop(object_path) # 注销回调 device_proxy.disconnect_by_func(on_device_properties_changed) # 如果之前是连接状态,计数减1 if device_proxy.get_cached_property("Connected"): update_connected_count(False) def init_bluez_listener(): # 获取系统DBus总线 bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) # 初始化ObjectManager客户端 object_manager = Gio.DBusObjectManagerClient.new_for_bus_sync( Gio.BusType.SYSTEM, Gio.DBusObjectManagerClientFlags.NONE, "org.bluez", "/", None, None, None ) # 注册ObjectManager的信号回调 object_manager.connect("interface-added", on_interfaces_added) object_manager.connect("interface-removed", on_interfaces_removed) # 处理初始设备列表 for obj in object_manager.get_objects(): if obj.has_interface("org.bluez.Device1"): object_path = obj.get_object_path() device_proxy = obj.get_interface("org.bluez.Device1") devices[object_path] = device_proxy device_proxy.connect("g-properties-changed", on_device_properties_changed) if device_proxy.get_cached_property("Connected"): update_connected_count(True) return object_manager if __name__ == "__main__": # 初始化GTK应用 app = Gtk.Application() # 初始化蓝牙监听 object_manager = init_bluez_listener() # 运行GTK主循环 app.run()
关键注意事项
- 不用开额外线程:GLib的GDBus会把所有DBus信号回调分发到GTK的主循环中执行,完全符合GTK的单线程模型,直接在回调里处理计数或UI更新就行。
- 及时注销回调:设备移除时一定要注销对应的
PropertiesChanged回调,不然会导致无效引用和内存泄漏。 - 避免耗时操作:回调里别做太耗时的事,会阻塞GTK主循环导致UI卡顿。如果有需要处理的耗时逻辑,用
GLib.idle_add把它放到主循环的空闲时段执行,或者用线程(但线程里不能直接操作UI,必须通过GLib.idle_add转发)。
内容来源于stack exchange
相关产品推荐
相关产品推荐

