最近公司项目中有用到蓝牙游戏手柄,需要连接蓝牙游戏手柄操作机顶盒,所以这里将开发思路分享下~
获取蓝牙管理器
BluetoothAdapter.getDefaultAdapter();
获取到蓝牙设备启用/禁用状态以及启用和禁用的相关方法
mBluetoothAdapter.isEnabled()
mBluetoothAdapter.disable();
mBluetoothAdapter.enable();
开始蓝牙设备的扫描/取消扫描/获取扫描状态
mBluetoothAdapter.isDiscovering()
mBluetoothAdapter.startDiscovery();
mBluetoothAdapter.cancelDiscovery()
蓝牙设备的配对/移除配对
BluetoothDevice.createBond();
BluetoothDevice.removeBond();
获取到蓝牙设备的配对状态以及常见的几种配对状态
BluetoothDevice.getBondState()
BluetoothDevice.BOND_BONDED
BluetoothDevice.BOND_BONDING
BluetoothDevice.BOND_SUCCESS
获取到已配对的蓝牙设备(即使设备关机以后也能获取到上次配对过的设备)
mBluetoothAdapter.getBondedDevices();
获取到蓝牙设备的代理(特别是type参数,个人开发的时候使用的是4,人机交互设备,还有另外几种)主要用来连接蓝牙游戏手柄的
BluetoothAdapter.getProfileProxy(Context arg0,ServiceListener arg1, int type)
关闭蓝牙设备的代理
BluetoothAdapter.closeProfileProxy(int arg0,BluetoothProfile profile)
相关的系统广播:
BluetoothAdapter.ACTION_DISCOVERY_STARTED
扫描开始
BluetoothAdapter.ACTION_DISCOVERY_FINISHED
扫描结束
BluetoothDevice.ACTION_FOUND
发现了蓝牙设备,可通过固定Key值获取相应的蓝牙设备,参考代码如下
BluetoothDevice device=(BluetoothDevice)intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
BluetoothDevice.ACTION_BOND_STATE_CHANGED
设备的配对状态发生了改变,例如配对成功了或者失败了,也通过固定Key值获取相应的蓝牙设备(代码参考上面)
BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED
BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED
这两种广播的含义基本一致,表示设备的连接状态发生改变,如连接成功了,连接断开了,也是携带有相应的蓝牙设备,可通过上面的参考代码获取
蓝牙开发的大致思路是:
第一步,注册相应的系统广播
第二步,获取蓝牙管理器,开始扫描
第三步,接收到发现设备的广播后,将蓝牙设备添加到设备列表中,并且更新UI
第四步,接收到扫描结束广播后,给出提示并更新UI
当用户选择一个蓝牙设备后,先进行蓝牙配对,如果配对失败则给出提示,如果配对成功则在接收到配对成功广播后进行连接即可
=========================谈谈开发过程中遇到的几个问题=========================
1:搜索到蓝牙设备后如何识别蓝牙设备的类型从而加载相应的图标?
发现类HidProfile中的getDrawableResource() 和getHidClassDrawable()方法,再继续深挖,发现类CachedBluetoothDevice中 fillData() 和 fetchBtClass()方法;最后在
BluetoothDevicePreference类中发现了 getBtClassDrawable()方法,这个方法就是问题的答案的核心所在,附上截图在下面
先是通过bluetoothDevice.getBluetoothClass()大致判断下分类,如果还不能确定下来,再通过bluetoothDevice.getBluetoothClass().getDeviceClass()再进行判断,如果再识别不了就返回默认的蓝牙图标了。
2:如何连接上歌华的蓝牙游戏手柄,并且保持长连接(即使退出了连接界面)?
获取到可输入蓝牙设备的代理的方法为(第三个参数是蓝牙设备的类型需要对应):
bluetoothAdapter.getProfileProxy(context,ServiceListener,BluetoothProfile.INPUT_DEVICE);
BluetoothProfile类中的定义的蓝牙设备有多种有HEADSET,A2DP,HEALTH,INPUT_DEVICE,PAN等,对这种蓝牙游戏手柄需要指定为INPUT_DEVICE;然后在监听(ServiceListener)回调中将代理强制转换为BluetoothInputDevice对象,然后通过它去连接游戏手柄即可;可是后来发现了个问题:如果退出蓝牙连接的界面和手柄的连接就断开了。这个问题也是纠结了很久,最后翻阅了设置的一些源码,才找到些线索,原来代理是始终要保持连接的,于是启动了一个服务(Service)并在服务中去连接可输入蓝牙设备的代理才保住了它的长连接,这个问题也就解决了
3:如何保证第一次连接过的蓝牙设备能在开机关机后第二次开机仍然能显示在蓝牙列表中?
先要将配对过的蓝牙设备的地址保存在SharedPreferences中,然后
mBluetoothAdapter.getBondedDevices();获取到已配对的蓝牙设备,进行对比地址,如果保存的地址和获取到的设备地址一致的话可以将该获取到的设备添加到列表中,参考代码如下
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
题外扩展:蓝牙中的文件传输
刚开始接触蓝牙的时候,个人对于蓝牙的文件传输(两人手机蓝牙连接上后就可以分享图片等文件了)是比较好奇的,就是想看看它到底是怎么实现的,原来它其实用的是OPP协议(蓝牙APK中包含有OPP的子文件夹),用Socket去建立连接,用Stream去写入和读取文件数据,这样看来也是比较简单~~ 然后我把一些关键的API分享到了下面,感兴趣的同学可以自己再去研究下细节,当然也可以开发出这样类似的Demo
蓝牙的MAC地址相当于Socket的IP地址, 蓝牙的特征值(UUID)相当于Socket的端口
文件传输的UUID :00001106-0000-1000-8000-00805F9B34FB
服务端:BluetoothServerSocket
客户端:BluetoothSocket
输入流:mBluetoothSocket.getInputStream();
输出流:mBluetoothSocket.getOutputStream()