在 D-Bus 编程中,代理对象(proxy object)是一个方便的抽象,用于简化与远程 D-Bus 服务的交互。代理对象代表远程 D-Bus 服务的某个对象,使得你可以像调用本地对象的方法一样调用远程对象的方法、获取属性以及监听信号。虽然使用代理对象并不是必须的,但它极大地简化了 D-Bus 编程,特别是对于复杂的 D-Bus 接口。
代理对象的作用
- 方法调用简化: 代理对象提供了调用远程方法的简单接口,你可以直接调用代理对象的方法,而不需要手动构建和解析 D-Bus 消息。
- 属性访问简化: 代理对象允许你直接访问远程对象的属性,而不需要手动处理 D-Bus 的
Get
和Set
请求。 - 信号处理简化: 代理对象可以自动处理从远程对象发出的信号,简化信号监听和处理过程。
- 错误处理: 代理对象提供一致的错误处理机制,使得处理 D-Bus 方法调用和属性访问中的错误更加方便。
6、bluez提供的基于dbus的api
BlueZ 是 Linux 下的官方蓝牙协议栈,提供了丰富的 D-Bus 接口来管理蓝牙设备。BlueZ 中的主要接口和方法涉及设备发现、配对、连接以及管理 GATT 服务和特征。以下是 BlueZ 中的关键接口、方法及其之间的关系的详细说明。
(1)主要接口
- org.bluez.Adapter1
- 描述:表示蓝牙适配器,负责设备发现和管理。
- 常用方法:
StartDiscovery
:开始扫描周围的蓝牙设备。StopDiscovery
:停止扫描。RemoveDevice
:移除已配对的设备。
- 常用属性:
Address
:适配器的地址。Name
:适配器的名称。Powered
:适配器的电源状态。
- org.bluez.Device1
- 描述:表示蓝牙设备,可以配对和连接。
- 常用方法:
Connect
:连接到设备。Disconnect
:断开连接。Pair
:配对设备。CancelPairing
:取消配对过程。
- 常用属性:
Address
:设备的地址。Name
:设备的名称。Paired
:配对状态。Connected
:连接状态。
- org.bluez.GattService1
- 描述:表示 GATT 服务。
- 常用属性:
UUID
:服务的 UUID。Primary
:是否为主服务。Device
:所属设备。
- org.bluez.GattCharacteristic1
- 描述:表示 GATT 特征。
- 常用方法:
ReadValue
:读取特征值。WriteValue
:写入特征值。StartNotify
:开始通知。StopNotify
:停止通知。
- 常用属性:
UUID
:特征的 UUID。Service
:所属服务。Value
:当前特征值。
- org.bluez.GattDescriptor1
- 描述:表示 GATT 描述符。
- 常用方法:
ReadValue
:读取描述符值。WriteValue
:写入描述符值。
- 常用属性:
UUID
:描述符的 UUID。Characteristic
:所属特征。Value
:当前描述符值。
- org.freedesktop.DBus.ObjectManager
- 描述:用于管理和监控 D-Bus 对象,通常用于获取所有对象及其接口和属性。
- 常用方法:
GetManagedObjects
:获取所有受管理的对象及其接口和属性。
- 常用信号:
InterfacesAdded
:当新对象添加时发出信号。InterfacesRemoved
:当对象接口移除时发出信号。
(2)方法之间的关系和流程
以下是使用 BlueZ 进行 BLE 设备发现、连接和操作的常见流程:
- 启动设备发现
- 调用
org.bluez.Adapter1.StartDiscovery
开始扫描设备。 - 订阅
org.freedesktop.DBus.ObjectManager.InterfacesAdded
信号,获取新发现的设备。
- 调用
- 配对设备
- 在
InterfacesAdded
信号回调中,检查是否是目标设备。 - 调用
org.bluez.Device1.Pair
进行配对。
- 在
- 连接设备
- 配对成功后,调用
org.bluez.Device1.Connect
连接到设备。
- 配对成功后,调用
- 发现服务和特征
- 连接成功后,调用
org.freedesktop.DBus.ObjectManager.GetManagedObjects
获取设备的所有服务和特征。 - 遍历返回的对象,找到
org.bluez.GattService1
和org.bluez.GattCharacteristic1
。
- 连接成功后,调用
- 操作特征
- 使用
org.bluez.GattCharacteristic1.ReadValue
读取特征值。 - 使用
org.bluez.GattCharacteristic1.WriteValue
写入特征值。 - 使用
org.bluez.GattCharacteristic1.StartNotify
订阅通知。
- 使用
7、main_loop和dbus
“main_loop” 和 “dbus” 是两个在 Linux 环境下开发桌面应用和系统服务时经常会遇到的概念。
(1)Main Loop (主循环)
主循环是事件驱动编程的核心概念,广泛应用于图形用户界面(GUI)和其他需要持续处理事件的应用中。它的主要作用是不断地检查和处理事件,如用户输入、网络数据、定时器等。
在一个典型的主循环中,应用程序会做以下工作:
- 检查是否有任何事件发生(如鼠标点击、键盘输入)。
- 处理这些事件并执行相应的回调函数。
- 如果没有事件发生,则应用程序通常会等待(通常是阻塞操作),直到有事件需要处理。
在 Linux 环境下,许多 GUI 库如 GTK 和 Qt 都实现了自己的主循环。例如,GTK 使用 GMainLoop
进行事件处理。
(2)D-Bus (Desktop Bus)
D-Bus 是一个消息总线系统,用于在同一台机器上运行的多个程序之间进行通信。它的设计初衷是提供一个简单的方式,允许应用程序相互通信并协调工作。
D-Bus 有两个主要的总线:
- 系统总线:用于系统服务与应用程序之间的通信,例如网络管理器、音量控制等系统服务。
- 会话总线:用于同一用户会话中的应用程序之间的通信,例如桌面环境的组件。
D-Bus 的通信模型基于消息传递,包括方法调用、信号和属性。应用程序可以通过 D-Bus 接口来调用其他应用程序的方法、接收信号或查询/设置属性。
(3)Main Loop 和 D-Bus 的结合
在实际开发中,D-Bus 通常需要集成到应用程序的主循环中,以便处理来自 D-Bus 的消息。这种集成通常通过库来实现,例如:
- GLib(用于 GTK 应用):GLib 提供了对 D-Bus 的支持,可以将 D-Bus 消息处理集成到 GMainLoop 中。
- QtDBus(用于 Qt 应用):QtDBus 提供了对 D-Bus 的支持,可以将 D-Bus 消息处理集成到 Qt 的事件循环中。
例如,在一个使用 GLib 的应用程序中,您可以这样使用 D-Bus:
#include <glib.h>
#include <gio/gio.h>
int main(int argc, char *argv[]) {
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
GDBusConnection *connection;
GError *error = NULL;
connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
if (error != NULL) {
g_printerr("Error connecting to D-Bus: %s\n", error->message);
g_error_free(error);
return 1;
}
// 设置 D-Bus 信号处理等
g_main_loop_run(loop);
// 清理资源
g_main_loop_unref(loop);
g_object_unref(connection);
return 0;
}
在这个示例中,g_main_loop_run
会启动主循环,g_bus_get_sync
用于连接到会话总线,之后可以设置 D-Bus 信号处理等操作。
总之,main loop 和 D-Bus 在 Linux 环境下的桌面应用开发中是两个非常重要的组件,前者负责事件处理循环,后者提供了进程间通信机制。通过将 D-Bus 集成到主循环中,可以让应用程序更高效地处理系统事件和进程间通信。
8、GVariant格式
GVariant
是 GLib 库中的一种数据类型,用于在 D-Bus 上表示和传输复杂的数据结构。格式字符串 (a{oa{sa{sv}}})
描述了 GVariant
的一种复合结构,常用于表示对象树及其属性。这种格式在 BlueZ 和其他使用 D-Bus 的应用中非常常见,尤其是在实现 org.freedesktop.DBus.ObjectManager
接口时。
结构说明
( ... )
:表示一个元组。a{ ... }
:表示一个键值对的数组(字典)。o
:表示一个对象路径(D-Bus 中的对象路径以/
开头的字符串)。a{ ... }
:嵌套的键值对数组。s
:表示一个字符串。a{ ... }
:再次嵌套的键值对数组。v
:表示一个变体(可以是任意类型)。
解释 (a{oa{sa{sv}}})
- 最外层的括号
()
表示这是一个元组。 a{oa{sa{sv}}}
描述了元组中的一个元素,这是一个数组,每个元素是一个键值对,其中键是对象路径(o
),值是另一个字典(a{sa{sv}}
)。a{sa{sv}}
描述了对象路径对应的值,这是一个字典,其中键是接口名称(s
),值是属性字典(a{sv}
)。a{sv}
描述了属性字典,其中键是属性名称(s
),值是属性值(v
,表示变体类型)。
bluez和dbus的开发中,很多该格式的操作,glib库中提供了很多该格式的函数方法,具体情况具体分析使用。
(1)具体示例
假设我们有如下的 GVariant
数据表示一个蓝牙设备和其服务:
(
{
"/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX" : { // 设备对象路径
"org.bluez.Device1" : { // 接口
"Address" : <"XX:XX:XX:XX:XX:XX">, // 属性
"Name" : <"DeviceName">
}
},
"/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0001" : { // 服务对象路径
"org.bluez.GattService1" : { // 接口
"UUID" : <"00001800-0000-1000-8000-00805f9b34fb">, // 属性
"Primary" : <true>
}
}
}
)
(2)解析示例
void parse_managed_objects(GVariant *result) {
GVariantIter *iter;
gchar *object_path;
GVariant *interfaces;
// 解析结果
g_variant_get(result, "(a{oa{sa{sv}}})", &iter);
// 遍历每个对象路径
while (g_variant_iter_next(iter, "{oa{sa{sv}}}", &object_path, &interfaces)) {
g_print("Object Path: %s\n", object_path);
GVariantIter *iface_iter;
gchar *interface;
GVariant *props;
// 解析接口
g_variant_get(interfaces, "a{sa{sv}}", &iface_iter);
while (g_variant_iter_next(iface_iter, "{sa{sv}}", &interface, &props)) {
g_print(" Interface: %s\n", interface);
GVariantIter *prop_iter;
gchar *prop_name;
GVariant *prop_value;
// 解析属性
g_variant_get(props, "a{sv}", &prop_iter);
while (g_variant_iter_next(prop_iter, "{sv}", &prop_name, &prop_value)) {
gchar *value_str = g_variant_print(prop_value, TRUE);
g_print(" %s: %s\n", prop_name, value_str);
g_free(value_str);
g_variant_unref(prop_value);
g_free(prop_name);
}
g_variant_iter_free(prop_iter);
g_variant_unref(props);
g_free(interface);
}
g_variant_iter_free(iface_iter);
g_variant_unref(interfaces);
g_free(object_path);
}
g_variant_iter_free(iter);
}
9、Dbus接口
org.freedesktop.DBus.ObjectManager
和 org.freedesktop.DBus.Properties
是两个不同的 D-Bus 接口,分别用于管理对象和处理属性。它们在 BlueZ(Linux 上的 Bluetooth 栈)以及其他基于 D-Bus 的系统中广泛使用。以下是它们的详细介绍。
(1)org.freedesktop.DBus.ObjectManager
org.freedesktop.DBus.ObjectManager
接口用于管理对象树,主要用于枚举和监视对象的生命周期变化。它有两个主要方法:
-
GetManagedObjects
-
描述:返回对象路径和它们对应的接口和属性。
-
方法签名:
a{oa{sa{sv}}}
-
示例代码:
GVariant *result = g_dbus_connection_call_sync( connection, "org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", NULL, G_VARIANT_TYPE("(a{oa{sa{sv}}})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error ); if (error != NULL) { g_printerr("Error getting managed objects: %s\n", error->message); g_error_free(error); return; }
-
信号
- InterfacesAdded
- 描述:在新对象添加到对象树时发出信号。
- 信号签名:
(oa{sa{sv}})
- InterfacesRemoved
- 描述:在对象从对象树中移除时发出信号。
- 信号签名:
(oas)
(2)org.freedesktop.DBus.Properties
org.freedesktop.DBus.Properties
接口用于获取和设置对象属性,并监听属性变化。它有几个主要方法和一个信号:
方法
-
Get
-
描述:获取对象的特定属性值。
-
方法签名:
(ss) -> (v)
-
示例代码:
GVariant *result = g_dbus_connection_call_sync( connection, "org.bluez", object_path, "org.freedesktop.DBus.Properties", "Get", g_variant_new("(ss)", interface_name, property_name), G_VARIANT_TYPE("(v)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error ); if (error != NULL) { g_printerr("Error getting property: %s\n", error->message); g_error_free(error); return; } GVariant *property_value = g_variant_get_child_value(result, 0); // 处理属性值 g_variant_unref(property_value); g_variant_unref(result);
-
-
Set
- 描述:设置对象的特定属性值。
- 方法签名:
(ssv) -> ()
-
GetAll
- 描述:获取对象的所有属性值。
- 方法签名:
(s) -> (a{sv})
信号
- PropertiesChanged
- 描述:当对象的属性发生变化时发出信号。
- 信号签名:
(sa{sv}as)
(3)信号签名
上述中的信号签名可以理解为调用方法的输入参数类型(箭头后面的表示输出参数类型)
(ss) -> (v)
是 D-Bus 方法调用的签名格式,描述了方法的输入参数和返回值类型。这种签名在 org.freedesktop.DBus.Properties
接口的 Get
方法中非常常见。
签名解释
(ss)
:表示方法的输入参数是两个字符串。->
:表示从输入参数到返回值的转换。(v)
:表示方法的返回值是一个变体类型。
具体来说,这个签名表示一个方法,它接收两个字符串参数并返回一个变体。
org.freedesktop.DBus.Properties.Get
方法
这个签名用于 org.freedesktop.DBus.Properties
接口中的 Get
方法,该方法用于获取某个对象属性的值。
- 方法名称:
Get
- 输入参数:
s
:接口名称s
:属性名称
- 返回值:
v
:属性值