BluetoothHeadset
提供蓝牙耳机支持,以便与手机配合使用。其中包括蓝牙耳机和免提(1.5版)配置文件。BluetoothProfile 的实现类
BlutoothA2dp
定义高质量音频如何通过蓝牙连接和流式传输,从一台设备传输到另一台设备。“A2DP”代表高级音频分发配置文件。是 BluetoothProfile 的实现类
BluetoothHealth
表示用于控制蓝牙服务的健康设备配置文件代理。 BluetoothProfile 的实现类。
BluetoothGatt
BluetoothProfile 的实现类。与低功耗蓝牙通信有关的配置文件代理
BluetoothHealthCallback
用于实现 BluetoothHealth 回调的抽象类。必须扩展此类并实现回调方法,以接收关于应用注册状态和蓝牙通道状态变化的更新内容。
BluetoothHealthAppConfiguration
表示第三方蓝牙健康应用注册的应用配置,以便与远程蓝牙健康设备通信
BluetoothProfile.ServiceListener
在 BluetoothProfile IPC 客户端连接到服务(即,运行特定配置文件的内部服务)或断开服务连接时向其发送通知的接口。
蓝牙权限
使用蓝牙必须声明权限 BLUETOOTH 才可以执行蓝牙通信。
设置蓝牙
- 获取蓝牙适配器
所有的蓝牙 Activity 都是需要 BluetoothAdapter 的。获取 BluetoothAdapter 调用 BluetoothAdapter 的静态方法 getDefaultAdapter()
方法。会返回一个表示设备自身的蓝牙适配器(蓝牙无线装置)的 BluetoothAdapter。
整个系统有一个蓝牙适配器,我们的应用可以通过 BluetoothAdapter 这个对象与之交互。如果 getDefaultAdapter()
返回 null 则说明该设备不支持蓝牙。
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null){
// 说明此设备不支持蓝牙操作
}
- 启用蓝牙
需要确认是否已经开启蓝牙isEnabled()
。返回 false 则说明蓝牙处于关闭状态。请求启用蓝牙。使用 ACTION_REQUEST_ENABLE
操作 Intent 调用 startActivityForResult()
将通过系统设置发出启用蓝牙的请求。也可以通过 mBluetoothAdapter.enable()
直接打开蓝牙。
// 没有开始蓝牙
if(!mBluetoothAdapter.isEnabled()){
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent,REQUEST_ENBLE_BT);
}
我们的应用也可以选择侦听 ACTION_STATE_CHANGED
广播 Intent。每当蓝牙状态发生变化时,系统会广播此 Intent。此广播包含额外字段 EXTRA_STATE
和 EXTRA_PREVIOUS_STATE
分别表示新的和旧的蓝牙状态。
查找设备
使用 BluetoothAdapter 可以通过设备发现或通过查询配对设备的列表来查找远程蓝牙设备。
设备发现是一个扫描过程,它会搜索局部区域内已启用蓝牙功能的设备,然后请求一些关于各台设备的信息。这个过程也称为发现、查询、扫描。局部区域内的蓝牙设备仅在其当前已启用可检测性时才会响应发现请求。如果设备可以检测到,它将通过共享一些信息(例如设备名称、类及其唯一MAC地址)来响应发现请求。利用此信息,执行发现的设备可以选择发起到被发现设备的连接。
在首次与远程设备建立连接后,将会自动向用户显示配对请求。设备完成配对后,将会保存关于该设备的基本信息(如 设备名称、MAC 地址)。并且可以使用 Bluetooth API 读取这些信息。利用远程设备的已知 Mac 地址可以随时向其发起连接,而不需执行发现操作(假定该设备处于有效范围内)。
被配对和被连接之间存在差别。**被配对意味着两台设备知晓彼此的存在,具有可用于身份验证的共享链路密钥,并且能够与彼此建立加密连接。被连接意味着设备当前共享一个 RFCOMM 通道,并且能够向彼此传输数据。**当前的 Android Bluetooth API 要求对设备进行配对,然后才能建立 RFCOMM 连接(在使用 Bluetooth API 发起加密连接时,会自动执行配对)。Android 设备是默认处于不可检测状态的。
查询配对的设备
在执行设备发现之前,有必要查询已配对的设备集合。用来了解设备是否处于已知状态。通过 getBondedDevices()
来实现,这将返回表示已配对设备的一组 BluetoothDevice
。
例如:我们可以查询所有已配对的设备,然后使用 ArrayAdapter 向用户显示每台设备的名称:
Set pairedDevices = mBlutooothAdapter.getBondedDevices();
if(pairedDevices.size() > 0){
for(BluetoothDevice device:pairedDevices){
// 把名字和地址取出来添加到适配器中
mArrayAdapter.add(device.getName()+“\n”+ device.getAddress());
}
}
要发起连接仅需要知道目标蓝牙设备的 Mac 地址就可以了。
发现设备
发现设备使用 startDiscovery()
该进程为异步进程。该方法会立刻返回一个布尔值,指示是否已成功启动发现操作。发现进程通常包含约 12 秒的查询扫描,之后对发现的设备进行扫描,以检索其蓝牙设备的名字。
我们的应用必须针对 ACTION_FOUND
Intent 注册一个 BroadcastReceiver,以便接受每台发现的设备的信息。针对每台设备,系统会广播 ACTION_FOUND Intent。这个 Intent 会携带额外的字段 EXTRA_DEVICE 和 EXTRA_CLASS。这两者分别包含 BluetoothDevice 和 BluetoothClass。
// 创建一个接受 ACTION_FOUND 的 BroadcastReceiver
private final BroadcastReceiver mReceiver = new BroadcastReceiver(){
public void onReceive(Context context,Intent intent){
String action = intent.getAction();
// 当 Discovery 发现了一个设备
if(BluetoothDevice.ACTION_FOUND.equals(action)){
// 从 Intent 中获取发现的 BluetoothDevice
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 将名字和地址放入要显示的适配器中
mArrayAdapter.add(device.getName + “\n” + device.getAddress());
}
}
};
// 注册这个 BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReiver,filter);
// 在 onDestroy 中 unRegister
注意 执行 discovery 对于蓝牙适配器来说是一个非常繁重的过程,并且会消耗大量资源。在找到要连接的设备后,要确保使用 cancelDiscovery()
来停止发现,然后尝试连接。如果您已经和某台设备进行连接,那么这个时候执行发现操作会大幅度的减少此连接可用的带宽!因此不应该在处于连接状态的时候执行发现操作!
启用可检测性
如果我们希望我们的设备是可以被其他设备检测到的,可以使用 ACTION_REQUEST_DISCOVERABLE
来操作 Intent 调用 startActivityForResult(Intent,int)
。这样会通过系统设置发出启用可检测到模式的请求(无需停止我们的应用)。默认情况下,设备会变为可检测状态并且持续 120 秒钟。我们还可以通过 **EXTRA_DISCOVERABLE_DURATION**
Intent Extra
**来定义不同的持续时间。可以设置的最大持续时间为 3600 秒。**值为 0 表示始终可以被检测到。任何小于 0 或者大于 3600 的值都会自动设置为 120 秒钟。
例如:
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
startActivityForResult(discoverableIntent);
将显示对话框,请求用户允许将设备设为可检测到。如果用户响应为 YES,则设备将变为可检测到并持续指定的时间量。然后您的 Activity 将会收到对 onActivityResult() 回调的调用,其结果代码等于设备可检测到的持续时间。如果用户响应 NO 或者出现错误,结果代码为 RESULT_CANCELED
如果设备没有打开蓝牙,则启用设备可检测性的时候会自动启用蓝牙。
设备将在分配的时间内以静默方式保持可检测到模式。如果我们希望在可检测到模式发生变化时收到通知,可以利用 ACTION_SCAN_MODE_CHANGED
Intent 注册 BroadcastReceiver。它将包含额外字段 EXTRA_SCAN_MODE 和 EXTRA_PREVIOUS_SCAN_MODE,两者分别告诉我们新的和旧的扫描模式。每个字段可能包括SCAN_MODE_CONNECTABLE_DISCOVERABLE(可检测到模式)、SCAN_MODE_CONNECTABLE(未处于可检测模式但可以接受连接)、SCAN_MODE_NOE(未处于可检测到模式并且无法连接)
连接设备
要在两台设备上的应用之间创建连接,必须同时实现服务端和客户端机制,因为其中一台设备必须开放服务器套接字,而另一台设备必须发起连接(使用服务器设备的 MAC 地址发起连接)。当服务器和客户端在同一 RFCOMM 通道上分别拥有已连接的 BluetoothSocket 时,二者将被视为彼此连接。在这种情况下每台设备都能获得输入和输出流式传输,并且可以开始传输数据。
服务端和客户端分别以不同的方式来获得 BluetoothSocket 。服务器将在传入连接被接受时收到套接字。客户端将在其打开到服务器的 RFCOMM 通道时收到该套接字。
一种实现方式是自动将每台设备准备为一个服务器,从而使每台设备开发一个服务器套接字并侦听连接。然后任一设备可以发起与另一台设备的连接,并成为客户端。或者其中一台设备可显示“托管”连接并按需开放一个服务器套接字,从而另一台设备则直接发起连接。
在连接之前如果两个设备没有配对,则系统会自动发出配对请求
连接为服务器
当连接两台设备时,其中一台必须保持开发的 BluetoothServerSocket
来充当服务器,用于监听传入的连接请求,在接受了请求后提供一个已经连接的 BluetoothSocket
。从 BluetoothServerSocket
连接获取 BluetoothSocket
后就可以调用 close 来关闭这个等待了。
关于 UUID
通用唯一标识符(UUID),用于表示唯一标识信息的字符串ID,128位。可以使用网络上众多的随机 UUID 生成器,然后通过 formString(String)
来初始化一个 UUID。
服务器套接字接受连接的基本过程
- 通过
listenUsingRfcommWithServiceRecord(String,UUID)获取 BluetoothServerSocket
字符串是我们自己定义的服务的可识别名称,可以直接使用包名。系统会自定将其写入到设备上的新服务发现协议(SDP)数据库条目中。UUID 也在 SDP 中,作为与客户端设备连接协议的匹配规则。只有客户端和这里的UUID 一样了才可以会被连接
accept()
侦听连接请求
阻塞调用,将在连接被接受或者发生异常的时候返回,操作成功后,会返回 BluetoothSocket
。
- 除非要接受更多的连接,否则调用
close()
来关闭这个监听
这样会释放服务器套接字及其所有资源,但不会关闭已经连接的 BluetoothSocket。与 TCP/IP 不同的是,RFCOMM 一次只允许每个通道有一个已经连接的客户端。
放在子线程中去执行。
例子:
private class AcceptThread extend Thread{
private final BluetoothServerSocket mServerSocket;
public AcceptThread(){
mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID);
}
public void run(){
BluetoothSocket socket = null;
while(true){
socket = mServerSocket.accept();
if(socket!=null){
// 自定义方法
manageConnectedSocket(socket);
mServerSocket.close();
break;
}
}
}
public void cancle(){
mServerSocket.close();
}
}
连接为客户端
要想和保持开发服务器套接字的设备建立连接,必须首先要获取该设备的 BluetoothDevice 对象。然后使用这个对象来获取 BluetoothSocket 并发起连接。
客户端连接的基本过程
- 通过 BluetoothDevice 的 createRfcommSocketToServiceRecord(UUID) 获取 BluetoothSocket 对象
这里的 UUID 要和服务器端的一致
- 通过 connect() 发起连接
执行此方法后,系统将会在远程设备上执行 SDP 查找,来匹配 UUID。如果查找成功了并且远程设备接受了该连接,它将共享 RFCOMM 通道在连接期间使用。这个时候 connect() 就会返回。这个方法也是阻塞的,如果失败或者超时(12秒之后),将引发异常。
调用 connect() 的时候要确保客户端没有执行发现操作。如果执行了会大幅度降低连接的速度,增加失败的可能性
例子
private class ConnectThread extend Thread{
private BluetoothDevice mDevice;
private BluetoothSocket mSocket;
public ConnectThread(BluetoothSocket device){
mDevice = device;
// 这里的 UUID 需要和服务器的一致
mSocket = device.createRfcommSocketToServiceRecord(My_UUID);
}
public void run(){
// 关闭发现设备
mBluetoothAdapter.cancelDiscovery();
try{
mSocket.connect();
}catch(IOException connectException){
try{
mSocket.close();
}catch(IOException closeException){
文末
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。
进阶学习视频
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
进阶学习视频
[外链图片转存中…(img-80JQR8jy-1714697233739)]
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-axW983mg-1714697233739)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!