不喜欢把一些简单的东西说成架构,在我心中架构是很大的一个东西,所以下面说SDK,就不说架构这么玄乎,就叫结构好了。
本文分两部分,头半篇是讲android ble的结构的,对ble不感兴趣的可以直接看下半篇。
android ble接口的结构
android的蓝牙接口是通过系统服务提供的,这个服务是系统服务,如果root 手机会在/system/app里面会看到,类似Bluetooth.apk这样的APK。
既然是服务,又是通过APK方式提供的,必然就是通过AIDL方式调用的了。
所以,蓝牙里面那么多的类,虽然看起来都是业务的类,其实都只是个Wrapper,主要就是封装AIDL调用。
BLE涉及的几个类
BluetoothDevice
其实看定义extends Parcelable就知道,这个所谓的Device,并不是真的device,就是个数据对象而已,内部持有了MacAddress和DeviceName(都是String)。
BluetoothDevice.class
可以通过connect接口+macAddress拿到BluetoothGatt对象,这是一个Ble通讯对象,下面会介绍。
connect函数 connect函数具体实现BluetoothGatt
可以用来通讯的BLE对象,跟BluetoothDevice略有不同。它是一个Wrapper,可以理解为一个句柄,不是纯数据对象,且持有了callback(所以肯定不能串行化,无法在Activity之间传递,这点很重要)。
这个类主要干了两个事
1.从BluetoothService申请一个ID,以后通过这个ID访问的蓝牙设备都是BluetoothDevice.connect参数MacAddress的蓝牙设备
2.注册一个远程callback,以便从BluetoothService接受BLE回调,并且转换为对外部传参BluetoothGattCallback的回调(看下图)
注册远程回调到远程Service 远程转本地的回调Ble API结构特点
系统Ble接口这么设计必然是有原因的,主要是因为系统的蓝牙服务是共享的,那么相应的BLE外设的状态(特别这点)、数据发送,对系统所有的APP是应该是共享的。
因此,它的所有类几乎都是个Wrapper。因为BluetoothGatt是一个句柄,是无状态的,要查BluetoothGatt是否连上,只能通过注册到BluetoothGatt的callback得到的通知来存下来,或者通过BluetoothManager(一个连接到BluetoothService的Wrapper)。
BluetoothGatt.class小结
通过远程服务提供共享,中心化对象管理。通过Manager单例管理设备对象,设备对象全是无状态的Wrapper(可对同一MacAddress创建N个对象都不会冲突)。
最不好用的地方
BluetoothGatt不能串行化,无法在Activity间传递,当基于BluetoothGatt包装自定义的协议设备的时候,一是无法在Activity间传递,二是传递副本导致不同页面得到对象最终不是一个状态,如果不想对同一地址自定义设备创建N个,就得想办法做成静态全局的了。另外,BluetoothGatt无状态导致查询状态不直观太方便。
硬件/服务类SDK设计思路
主要分为单例方式和Manager方式。
单例方式又分为纯单例模式,纯Service模式,单例Wrapper+Service方式
Manager方式分为纯Manager方式,Manager+Service方式
名词都是自己发明的,可能不是学名,明白意思就好了:)
纯单例模式
最简单的方式了,所有业务都在一个单例实现。比如我要实现一个录音APP,
就写一个Recorder单例持有系统的AudioRecord。在App各个Activity之间可以操作。
有人会说了,为什么要写成单例这么复杂,直接用AudioRecord不就行了。
假如我在A的Activity启动了录音,在B Activity想操作录音相关的,由于AudioRecord不可跨页面传递(实际上即使可通过Intent传递,也是副本,不是引用),A和B页面之间的操作是无法共享状态的。
纯单例模式优点是足够简单,缺点是无法同时存在多个业务对象,没有生命周期管理。
这种方式在iOS平台的很多接口也有体现。
纯Service模式
跟单例模式一样,但是实现体是Local Service,还是上面录音APP的例子,对录音的封装就可以做成一个Service,需要用的Activity就Bind Service就可以了。
纯Service模式优点是系统会进行生命周期管理,很android的一种思路。缺点跟纯单例一样,无法同时存在多个业务对象。
单例Wrapper+Service方式
这种模式的Service一般用Remote Service方式多,如果没有多APP数据共享的需求用Local Service也可以。单例Wrapper本质上就是帮用户封装了对Service的远程调用。
比如 小米的支付SDK,集成的时候assets会放一个它的Service APK,如果用户没装过Service则会提示用户安装,如果装过了,就跟别的APP共享一个用户账号。
从调用的示例代码来看,很明显就是单例。注意它那里的数据Bundle,这个是可以序列化的,可以用AIDL传给远程Service。 小米SDK调用代码
纯Manager方式
这种方式其实很少用,如果是自己的APP,可以直接用Manager+Service方式,不过有些特殊的场景不得不这样设计。
比如 某种设备,假设叫XDevice,XDevice有状态,有缓存。当做成SDK给别人开发的时候,XDevice无法在页面传递了(传递副本也是无意义的),一般人也无法忍受很复杂的调用。
解决办法就是定义一个XDeviceManager单例,内含一个HashMap<Handler, XDevice>。通过它用设备句柄获取到XDevice,在页面间传递的时候就传递设备句柄,对于同一个句柄永远返回同一个XDevice对象。
其实全是静态全局的套路:(
Manager+Service方式
跟纯Manager方式类似,但是区别在于业务实际都是Service实现的,ManagerDevice都是Wrapper,同样的道理,如果要多App数据共享,就用远程Service,要求用户安装一个独立服务的APK。
其实系统的BLE就是类似这样的一个接口(不完全一致)。举例子?嗯,看图。
获取流就是Manager=>Info=>Client,但是它们其实还是Wrapper,有兴趣可以反编译看看。。。
总得来说Manager方式适用于有多个业务对象的情况,其余的思路跟单例方式是一样的。
网友评论