RxCentral: Uber的无缝蓝牙集成开源库

0
RxCentral: Uber的无缝蓝牙集成开源库

在优步,我们通过将用户与数字世界和现实世界连接起来的产品进行创新,使交通和外卖等服务尽可能简单。越来越多的这些产品都有一个共同的需求:使用蓝牙进行发现、连接和通信。为了实现新一代的创新,我们需要让工程师更容易实现蓝牙,并使跨平台设计能够跨应用程序重复。

进入RxCentral,这是uber开发的开源库Android (RxCentralBle和iOS (RxCBCentral),通过平台不可知的反应式设计,以可靠的、可重复的方式与蓝牙LE外设通信。RxCentral的响应式功能可以单独使用,也可以组合使用,用蓝牙LE外设编排优雅的响应式用户体验。

我们最初的整合将我们的司机伙伴的手机与下一代手机连接起来超级灯塔.Beacon将颜色匹配技术与先进的传感器套件相结合,以增强司机和合作伙伴和乘客的体验。为了实现其复杂的功能,信标需要高吞吐量和高可靠性,这将突破蓝牙的极限:RxCentral提供了这一功能,并在此过程中,改善了我们平台上的乘客和司机合作伙伴的交通体验,他们只能在应用程序内使用蓝牙,同时利用信标。

RxCentralBle一目了然

RxCentralBle允许我们快速实现四个主要功能:检测状态、扫描、连接和通信。每个功能都以反应式的方式执行;订阅可以启用功能,处理可以停止,这样工程师就可以设计出与现代反应式应用程序清晰集成的服务。

检测蓝牙

BluetoothDetector检测Android设备的蓝牙状态,确定蓝牙是否处于活动状态并可以连接:

BluetoothDetector BluetoothDetector;
一次性检测;

//使用检测器检测蓝牙状态
detection = bluetoothDetector
.enabled ()
.subscribe (
已启用-> {
//如果蓝牙未启用,告诉用户打开蓝牙

);

在我们的反应式范例中,我们可以处置我们的检测订阅以停止检测:

//停止蓝牙检测。
detection.dispose ();

扫描

扫描器告诉设备扫描蓝牙LE外设广告其可用性:

扫描仪扫描仪;
一次性扫描;

//使用扫描器扫描广告外设。
扫描=扫描仪
.scan ())
.subscribe (
scanata -> {
//我们找到了我们要找的外围设备

);

丢弃扫描器订阅将停止扫描其他蓝牙外设,自动清理我们自己后并释放所有扫描资源:

//停止扫描。
scanner.dispose ();

连接

RxCentralBle建立连接的代码与使用bluetthdetector和Scanner同样容易。基于上面的例子,我们可以扫描设备并使用一个ConnectionManager函数连接到它们:

ScanData ScanData;
ConnectionManager ConnectionManager
PeripheralManager PeripheralManager;
一次性连接;

//连接到设备。
connection = connectionManager
.connect (scanData)
.subscribe (
外围设备-> {
//将新连接的外设注入到PeripheralManager中。
peripheralManager.setPeripheral(外围);

);

如果我们开始看到一个模式正在发展,我们可以直接将我们的连接订阅断开并清理所有资源:

//断开设备连接。
connection.dispose ();

沟通

一旦上面的功能创建了到外设的蓝牙连接并将其交给PeripheralManager,我们就可以对操作进行排队,比如通用属性配置文件(GATT)读写,在外围设备上执行:

PeripheralManager PeripheralManager;
阅读读;
一次性操作;

//执行写操作。
operation = peripheralManager
.queueOperation(读))
.subscribe (
Bytes -> {
//从外设读取的字节数。
});

我们还保留了对操作的订阅,允许我们取消尚未运行的操作:

//取消读取。
operation.dispose ();

增强,反应蓝牙LE

RxCentralBle使以可靠的、可重复的方式编排复杂的动作序列变得容易。因为每个功能都遵循Observable契约,我们可以将它们组合成无缝的响应流,转化为与信标或任何其他蓝牙硬件支持的无缝用户体验。

检测、扫描和连接

我们可以检测状态,扫描外设,并在单个响应流上连接到外设:

BluetoothDetector BluetoothDetector;
扫描仪扫描仪;
ScanMatcher ScanMatcher;
ConnectionManager ConnectionManager
PeripheralManager PeripheralManager;
一次性连接;

//当蓝牙开启时,扫描并连接外设。
connection = bluetoothDetector
.enabled ()
.filter(启用->启用)
.switchMap(enabled -> scanner.scan())
.compose (scanMatcher.match ()
.switchMap(match -> connectionManager.connect(match))
.subscribe (
外围设备-> {
//将新连接的外设注入到PeripheralManager中。
peripheralManager.setPeripheral(外围);

);

由于连接到外设是一种常见的用例,RxCentralBle通过使用ConnectionManager和ScanMatcher的单一函数实现了这些连接。在这个典型的示例中,我们先连接并配置一个外设,然后再交给PeripheralManager进行通信。我们还实现了重试逻辑,以在连接错误发生时自动重新连接:

UUID服务;
ScanMatcher ScanMatcher;
ConnectionManager ConnectionManager
PeripheralManager PeripheralManager;
一次性连接;

//连接、配置和重试连接错误。
connection = connectionManager
.connect (scanMatcher))
.switchMap(外围- >
peripheral.registerNotification(服务)。flatMap(irr -> Single.just(外围设备))
.switchMap(外围- >
peripheral.requestMtu(512)。flatMap(mtu -> Single.just(外围设备))
.retryWhen(错误- >
错误。switchMap(错误-> {
if(错误实例连接错误){
返回Observable.just(真正的);
} else {
返回Observable.error(错误);
}}))
.subscribe (
外围设备-> {
peripheralManager.setPeripheral(外围);

);

重试任何失败操作的能力是RxCentralBle的一个关键特性,它处理重试而不需要管理或重置状态。

连接操作

通常情况下,外设实现了一个命令/响应协议,其中命令被写入一个特性,响应通过通知发送回移动应用程序:

UUID特征;
AbstractWrite writeCommand = new AbstractWrite(…){
@Override
protected SingleTransformer postWrite() {
return peripheralSingle -> peripheralSingle
.flatMap(外围->外围
.notification(特点)
.filter(bytes -> checkResponse(bytes))
.map(bytes -> true));

};

下面,我们每次连接到外设时执行一系列操作。如果系列中的任何操作失败,我们可以重试整个流或单个操作:

PeripheralManager PeripheralManager;
阅读read1;
阅读read2;
一次性操作;

//每次连接到外设时执行一个操作流
operations = peripheralManager
.connected ()
.filter(connected -> connected) //连接后开始操作。
.switchMap(连接- >
peripheralManager
.queueOperation (read1))
.switchMap (readResult1 - >
peripheralManager
.queueOperation (writeCommand)
. Retry(2)) //重试写命令最多两次。
.filter(writeResult -> writeResult)
.switchMap (writeSuccessful - >
peripheralManager
.queueOperation (read2))
. Retry(3) //重试整个流最多三次。
.subscribe (
readResult2 -> {
//最后,我们收到第二个读结果。

);

当使用RxCentralBle时,应用程序可以有多个流,如前所述,跨不同的线程;PeripheralManager提供了一个线程安全的FIFO操作队列。

使创新

在Uber,我们将RxCentralBle应用于多个生产和开发中的产品,以帮助用户在我们的平台上获得更无缝的交通体验。旗舰RxCentralBle集成用于灯塔

RxCentralBle的设计使得在ConnectionManager和PeripheralManager的核心实现之上扩展和组合功能变得容易。下面的图1展示了我们如何在信标上播放动画:

Uber信标和应用程序之间的蓝牙堆栈示意图
图1:这个设计展示了我们如何通过蓝牙向Uber beacon发送命令,在这种情况下,它可以播放动画,这样乘客就可以更容易地找到他们的车。

每个逻辑管理器(例如,AnimationManager和FileTransferManager)组成了向上扩展的反应流可见基于响应桥RxCentralBle提供平台级蓝牙api。

要在信标的后屏幕上播放动画,消费应用程序只需订阅由AnimationManager公开的流。这种模式允许工程师轻松地将信标集成到他们的应用程序中,而不必担心蓝牙的复杂性。

接下来是什么

我们设计RxCentral使蓝牙交互变得简单。它消除了与平台级蓝牙api和Android和iOS之间不同的系统设计作斗争的需要,让工程师充分利用蓝牙外设的功能。

我们选择开源RxCentral来帮助社区向前发展,并维护一个与所有外设都兼容的库,而不仅仅是Uber使用的那些外设。每一个蓝牙外设都带来了一系列新的挑战,收集反馈是改进RxCentral的关键。

工程师可以在以下Github页面提出问题或贡献新功能:

评论
前一篇文章 宣布2020年Uber AI驻地
下一篇文章 优步参加NeurIPS 2019
凯文·巴布科克
Kevin Babcock是一名Android工程师,拥有开发蓝牙LE连接设备、协议和端到端技术堆栈的丰富经验。在Uber,他领导Uber beacon的工程,并预见到Uber未来将拥有世界上最具影响力的物联网网络。
乔Soultanis
Joe Soultanis是一名iOS工程师,目前在Uber Freight工作,专注于搜索和市场动态。他此前曾领导Uber beacon的iOS工程,在那里他爱上了蓝牙和硬件。在业余时间,他喜欢举重,阅读有关技术的书籍,观看广受好评的电视节目,如《单身汉》、《幸存者》和《顶级厨师》。