精华内容
下载资源
问答
  • Android经典蓝牙开发全流程
    千次阅读
    2022-03-10 22:14:36

    一、基本介绍

      所谓蓝牙(Bluetooth)技术,实际上是一种短距离无线电技术,最初是由爱立信公司公司发明的。技术始于爱立信公司 1994 方案,它是研究在移动电话和其他配件间进行低功耗、低成本无线通信连接的方法。发明者希望为设备间的通讯创造一组统一规则(标准化协议)用来解决用户间相互不兼容的移动电子设备。
      1998年5月20日,索尼以立信、国际商业机器、英特尔、诺基亚及东芝公司等业界龙头创立“特别兴趣小组”(Special Interest Group SIG),即蓝牙技术联盟的前身,目标是开发一个成本低、效益高、可以在短距离范围内随意无线连接的蓝牙技术标准,是负责蓝牙规范的制定和推广的国际组织。

    在这里插入图片描述

      蓝牙发展至今经历了多个版本的更新,1.1、1.2、2.0、2.1、3.0、4.0、4.1、4.2、5.0等。其中,将1.x~3.0之间的版本称之为经典蓝牙,4.x开始的蓝牙称之为低功耗蓝牙,也就是蓝牙ble。根据应用、协议类型等,可以对蓝牙进行以下分类:

    在这里插入图片描述


    二、经典蓝牙API介绍

      Android平台包含蓝牙网络堆栈支持,此支持能让设备以无线方式与其他蓝牙设备交换数据。应用框架提供通过Android Bluetooth API访问蓝牙功能的权限。这些API允许应用以无线方式连接到其他蓝牙设备,从而实现点到点和多点无线功能。Android应用可通过Bluetooth API执行以下操作:

    • 扫描其他蓝牙设备
    • 查询本地蓝牙适配器的配对蓝牙设备
    • 建立 RFCOMM 通道
    • 通过服务发现连接到其他设备
    • 与其他设备进行双向数据传输
    • 管理多个连接

    以下对经典蓝牙开发相关的API进行介绍:

    1、BluetoothAdapter类

      BluetoothAdapter代表了移动设备的本地的蓝牙适配器, 通过该蓝牙适配器可以对蓝牙进行基本操作, 例如 : 启动设备发现,获取已配对设备,通过mac蓝牙地址获取蓝牙设备等。

    (1)获取本地蓝牙适配器实例

    方法定义:

    /**
     * 作用:
     *	获取本地蓝牙适配器实例
     * 参数:
     *	无
     * 返回:
     *	如果设备具备蓝牙功能,返回BluetoothAdapter 实例;否则,返回null对象。
     */
    public static synchronized BluetoothAdapter getDefaultAdapter();
    

    使用说明:

    1、获取默认本地蓝牙适配器的句柄。目前Android仅支持一个蓝牙适配器,但该API可以扩展为支持更多。

    (2)打开蓝牙

    方法定义:

    /**
     * 作用:
     *	打开蓝牙
     * 参数:
     *	无
     * 返回:
     *	如果蓝牙开始打开,则返回true;如果蓝牙打开发生问题,则返回false。
     */
    public boolean enable();
    

    使用说明:

    1、需要 BLUETOOTH_ADMIN权限。
    2、该方法将不经过用户同意,直接启用底层蓝牙硬件,并启动所有蓝牙系统服务。由于不同Android设备系统的实现不同,所以部分Android系统在调用该方法时也会弹框请求用户同意。
    3、打开蓝牙,还可以通过调用startActivityForResult方法,使用ACTION_REQUEST_ENABLE意图来实现,此方法将弹出对话框,请求允许打开蓝牙。可以在Activity中的onActivityResult()方法中处理操作结果。
    4、该方法是一个异步调用:它将立即返回结果。如果此调用返回true,则适配器状态将立即从STATE_OFF转换为STATE_TURNING_ON,并且稍后过渡到STATE_OFF或STATE_ON 。如果此调用返回false,则说明出现问题阻止适配器开启,例如设备处于飞行模式,或者蓝牙已打开。因此还应当监听ACTION_STATE_CHANGED广播,以跟踪后续蓝牙状态更改。

    (3)关闭蓝牙

    方法定义:

    /**
     * 作用:
     *	关闭蓝牙
     * 参数:
     *	无
     * 返回:
     *	如果蓝牙开始关闭,则返回true;如果蓝牙关闭发生问题,则返回false。
     */
    public boolean disable();
    

    使用说明:

    1、需要 BLUETOOTH_ADMIN权限。
    2、该方法将不经过用户同意,关闭所有蓝牙连接,停止蓝牙系统服务并关闭底层蓝牙硬件。由于不同Android设备系统的实现不同,所以部分Android系统在调用该方法时也会弹框请求用户同意。
    3、该方法是一个异步调用:它将立即返回结果。如果此调用返回true,则适配器状态将立即从STATE_ON转换为STATE_TURNING_OFF,并且稍后过渡到STATE_OFF或STATE_ON 。如果此调用返回false,则说明出现问题阻止适配器关闭,例如适配器已关闭。因此还应当监听ACTION_STATE_CHANGED广播,以跟踪后续蓝牙状态更改。

    (4)验证蓝牙MAC地址

    方法定义:

    /**
     * 作用:
     *	验证蓝牙设备MAC地址是否有效。
     * 参数:
     *	address:蓝牙MAC地址,字母必须大写,例如:"00:43:A8:23:10:F1"。
     * 返回:
     *	如果蓝牙MAC地址有效,则返回true;否则返回false。
     */
    public static boolean checkBluetoothAddress(String address);
    

    使用说明:

    (5)获取本地蓝牙MAC地址

    方法定义:

    /**
     * 作用:
     *	获取本地蓝牙适配器的硬件地址(MAC地址)
     * 参数:
     *	无
     * 返回:
     *	本地的硬件地址,例如:"00:11:22:AA:BB:CC"。
     */
    public String getAddress();
    

    使用说明:

    1、需要 BLUETOOTH权限。

    (6)获取蓝牙绑定列表

    方法定义:

    /**
     * 作用:
     *	获取与本机蓝牙所有绑定的远程蓝牙信息。
     * 参数:
     *	无
     * 返回:
     *	将本地蓝牙适配器绑定的一组BluetoothDevice对象返回。若出现错误返回null。
     */
    public Set<BluetoothDevice> getBondedDevices();
    

    使用说明:

    1、需要 BLUETOOTH权限。
    2、若蓝牙未打开,将返回空集。

    (7)获取蓝牙名称

    方法定义:

    /**
     * 作用:
     *	获取本地蓝牙适配器的蓝牙名称。
     * 参数:
     *	无
     * 返回:
     *	本地蓝牙名称。若出现错误,返回null。
     */
    public String getName();
    

    使用说明:

    1、需要 BLUETOOTH权限。

    (8)设置蓝牙名称

    方法定义:

    /**
     * 作用:
     *	设置本地蓝牙适配器的蓝牙名称。
     * 参数:
     *	name:蓝牙名称。
     * 返回:
     *	设置成功返回true,否则返回false。
     */
    public boolean setName(String name);
    

    使用说明:

    1、需要 BLUETOOTH_ADMIN权限。
    2、如果蓝牙未打开,该方法将返回false。
    3、尽管许多远程设备只能显示前40个字符,而有些可能仅限于20个,但有效的蓝牙名称最多使用UTF-8编码为248个字节。

    (9)获取蓝牙扫描模式

    方法定义:

    /**
     * 作用:
     *	获取本地蓝牙适配器的当前蓝牙扫描模式。
     * 参数:
     *	无
     * 返回:
     *	当前蓝牙适配器的蓝牙扫描模式。
     */
    public int getScanMode();
    

    使用说明:

    1、需要 BLUETOOTH权限。
    2、蓝牙扫描模式确定本地蓝牙适配器是否可被远程蓝牙设备连接和发现。
    3、如果蓝牙未打开,此方法将返回SCAN_MODE_NONE。

    蓝牙扫描模式:

    名称值(int)含义
    SCAN_MODE_NONE20该设备不能扫描以及被扫描。
    SCAN_MODE_CONNECTABLE21该设备可以扫描其他蓝牙设备。
    SCAN_MODE_CONNECTABLE_DISCOVERABLE23该设备既可以扫描其他设备,也可以被其他设备扫描发现。

    (10)获取蓝牙适配器状态

    方法定义:

    /**
     * 作用:
     *	获取本地蓝牙适配器的当前状态。
     * 参数:
     *	无
     * 返回:
     *	当前蓝牙适配器状态。
     */
    public int getState();
    

    使用说明:

    1、需要 BLUETOOTH权限。

    蓝牙适配器状态:

    名称值(int)含义
    STATE_OFF10表示本地蓝牙适配器已关闭
    STATE_TURNING_ON11表示本地蓝牙适配器正在打开
    STATE_ON12表示本地蓝牙适配器已开启,并可供使用
    STATE_TURNING_OFF13表示本地蓝牙适配器正在关闭

    (11)蓝牙是否打开

    方法定义:

    /**
     * 作用:
     *	判断当前蓝牙适配器是否打开
     * 参数:
     *	无
     * 返回:
     *	若蓝牙为打开状态,则返回true,否则返回false。
     */
    public boolean isEnabled();
    

    使用说明:

    1、需要 BLUETOOTH权限。
    2、如果蓝牙正处于打开状态并可用,则返回true值,getState()==STATE_ON 等价。

    (12)蓝牙是否正在扫描

    方法定义:

    /**
     * 作用:
     *	判断蓝牙适配器是否正在处于扫描过程中。
     * 参数:
     *	无
     * 返回:
     *	若蓝牙处于扫描状态,则返回true;否则返回false。
     */
    public boolean isDiscovering();
    

    使用说明:

    1、需要 BLUETOOTH权限。
    2、若蓝牙未打开,该方法将返回false。
    3、扫描设备是一个重量级过程,不应在扫描时尝试建立连接,而此时已存在的蓝牙连接将获得有限制的带宽以及高延迟。

    (13)启动扫描

    方法定义:

    /**
     * 作用:
     *	开始扫描周边蓝牙设备。
     * 参数:
     *	无
     * 返回:
     *	若启动成功,返回true;否则返回false。
     */
    public boolean startDiscovery();
    

    使用说明:

    1、需要 BLUETOOTH_ADMIN权限。
    2、通常为12秒左右的查询扫描过程。
    3、这是一个异步调用,它会立即返回。注册ACTION_DISCOVERY_STARTED和ACTION_DISCOVERY_FINISHED广播以确定发现何时开始和完成的确切时间。注册ACTION_FOUND以便在发现远程蓝牙设备时收到通知。
    4、若蓝牙未打开,该方法将返回false。
    5、扫描设备是一个重量级过程,不应在扫描时尝试建立连接,而此时已存在的蓝牙连接将获得有限制的带宽以及高延迟。可以使用cancelDiscovery()取消扫描操作。

    (14)取消扫描

    方法定义:

    /**
     * 作用:
     *	取消蓝牙搜索操作
     * 参数:
     *	无
     * 返回:
     *	如果取消成功, 则返回true; 如果取消失败, 返回false。
     */
    public boolean cancelDiscovery()
    

    1、需要 BLUETOOTH_ADMIN权限。
    2、若蓝牙未打开,该方法将返回false。
    3、因为蓝牙搜索是一个重量级过程,会耗费大量资源,所以在连接远程蓝牙设备前,必须调用这个方法,取消搜索。

    (15)获取远程蓝牙设备

    方法定义:

    /**
     * 作用:
     *	获取给定蓝牙硬件地址的BluetoothDevice对象。
     * 参数:
     *	address:蓝牙MAC地址,字母必须大写,例如:"00:43:A8:23:10:F1"。
     * 返回:
     *	指定的远程蓝牙设备。
     */
    public BluetoothDevice getRemoteDevice(String address);
    

    使用说明:

    1、如果MAC无效无效,将抛出IllegalArgumentException异常。

    (16)创建不安全的蓝牙服务套接字

    方法定义:

    /**
     * 作用:
     *	创建一个正在监听的不安全的带有服务记录的无线射频通信(RFCOMM)蓝牙端口。
     * 参数:
     *	name:SDP记录下的服务器名,可以是任意字符串。
     *	uuid:SDP记录下的UUID。
     * 返回:
     *	BluetoothServerSocket对象。
     */
    public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid);
    

    使用说明:

    1、需要 BLUETOOTH权限。
    2、系统将分配一个未使用的RFCOMM通道进行侦听。
    3、当发生错误时,例如蓝牙不可用、权限不足、通道被占用等,将抛出IOException异常。
    4、通过此方式创建的蓝牙服务套接字是不安全的,连接时不需要进行配对。

    (17)创建安全的蓝牙服务套接字

    方法定义:

    /**
     * 作用:
     *	创建一个正在监听的安全的带有服务记录的无线射频通信(RFCOMM)蓝牙端口。
     * 参数:
     *	name:SDP记录下的服务器名,可以是任意字符串。
     *	uuid:SDP记录下的UUID。
     * 返回:
     *	BluetoothServerSocket对象。
     */
    public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid);
    

    使用说明:

    1、需要 BLUETOOTH权限。
    2、系统将分配一个未使用的RFCOMM通道进行侦听。
    3、当发生错误时,例如蓝牙不可用、权限不足、通道被占用等,将抛出IOException异常。
    4、通过此方式创建的蓝牙服务套接字是安全的,连接时需要进行配对。


    2、BluetoothDevice类

      BluetoothDevice对象代表了一个远程的蓝牙设备, 通过这个类可以查询远程设备的物理地址, 名称, 连接状态等信息。这个类实际上只是一个蓝牙硬件地址的简单包装,这个类的对象是不可变的。对这个类的操作, 会执行在远程蓝牙设备的硬件上。

    (1)获取蓝牙名称

    方法定义:

    /**
     * 作用:
     *	获取远程蓝牙设备的蓝牙名称。
     * 参数:
     *	无
     * 返回:
     *	成功则返回蓝牙名称,若出现问题则返回null。
     */
    public String getName();
    

    使用说明:

    1、需要BLUETOOTH权限。
    2、执行设备扫描时,本地适配器将自动检索远程名称,并将缓存它们。此方法仅从缓存中返回此设备的名称。

    (2)获取蓝牙MAC

    方法定义:

    /**
     * 作用:
     *	获取远程蓝牙设备的硬件地址
     * 参数:
     *	无
     * 返回:
     *	蓝牙硬件地址
     */
    public String getAddress();
    

    使用说明:

    (3)获取蓝牙绑定状态

    方法定义:

    /**
     * 作用:
     *	获取远程蓝牙设备的绑定状态
     * 参数:
     *	无
     * 返回:
     *	蓝牙绑定状态
     */
    public int getBondState();
    

    使用说明:

    1、需要BLUETOOTH权限。

    蓝牙绑定状态:

    名称值(int)含义
    BOND_NONE10远程设备未绑定。
    BOND_BONDING11正在与远程设备进行绑定。
    BOND_BONDED12远程设备已绑定。

    (4)绑定远程设备

    方法定义:

    /**
     * 作用:
     *	开始与远程蓝牙设备的绑定过程。
     * 参数:
     *	无
     * 返回:
     *	若成功开始绑定则返回true,否则返回false。
     */
    public boolean createBond();
    

    使用说明:

    1、需要BLUETOOTH_ADMIN权限。
    2、这是一个异步调用,它会立即返回。注册监听ACTION_BOND_STATE_CHANGED广播,当绑定过程完成时及时获取其结果通知。
    3、Android系统服务将处理必要的用户交互以确认并完成绑定过程。

    (5)创建不安全的蓝牙套接字

    方法定义:

    /**
     * 作用:
     *	创建不安全的蓝牙套接字。
     * 参数:
     *	uuid:用于查找RFCOMM通道的服务记录UUID
     * 返回:
     *	一个准备好外界连接的RFCOMM蓝牙服务端口
     */
    public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid);
    

    使用说明:

    1、需要BLUETOOTH权限。
    2、通信渠道将不会有认证的链接密钥,即它将受到中间人攻击。
    3、对于蓝牙 2.1 设备,链接将被加密,因为加密是强制性的。对于旧设备(蓝牙 2.1 之前的设备),链接不会被加密。
    4、它旨在与listenUsingInsecureRfcommWithServiceRecord(String, UUID)用于对等蓝牙应用。
    5、出现错误时,例如蓝牙不可用、权限不足,将抛出IOException异常。

    (6)创建安全的蓝牙套接字

    方法定义:

    /**
     * 作用:
     *	创建安全的蓝牙套接字。
     * 参数:
     *	uuid:用于查找RFCOMM通道的服务记录UUID
     * 返回:
     *	一个准备好外界连接的RFCOMM蓝牙服务端口
     */
    public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid);
    

    使用说明:

    1、需要BLUETOOTH权限。
    2、只有经过身份验证的套接字链接才可以使用此套接字。认证是指认证链路密钥,以防止中间人攻击。
    3、此套接字上的通信将被加密。
    4、这是为与对等蓝牙应用程序 listenUsingRfcommWithServiceRecord(String, UUID)配合使用而设计的。
    5、出现错误时,例如蓝牙不可用、权限不足,将抛出IOException异常。


    3、BluetoothServerSocket类

      BluetoothServerSocket是一个侦听蓝牙服务套接字。使用BluetoothServerSocket可以创建一个监听服务端口, 使用accept方法阻塞, 当该方法监测到连接的时候, 就会返回一个BluetoothSocket对象来管理这个连接。BluetoothServerSocket是线程安全的,close方法始终会立即中止正在进行的操作并关闭蓝牙服务套接字。需要BLUETOOTH权限。

    (1)阻塞等待连接(无超时)

    方法定义:

    /**
     * 作用:
     *	阻塞直到建立连接。
     * 参数:
     *	无
     * 返回:
     *	成功连接时连接的BluetoothSocket对象。
     */
    public BluetoothSocket accept();
    

    使用说明:

    1、一旦这个调用返回,它可以被再次调用来接受后续的传入连接。
    2、close()可用于从另一个线程中止此调用。
    3、发生错误时,例如该调用被中止、超时,将抛出IOException异常。

    (2)阻塞等待连接(有超时)

    方法定义:

    /**
     * 作用:
     *	阻塞直到建立连接或超时。
     * 参数:
     *	timeout:阻塞等待的超时时间。
     * 返回:
     *	成功连接时连接的BluetoothSocket对象。
     */
    public BluetoothSocket accept(int timeout);
    

    使用说明:

    1、一旦这个调用返回,它可以被再次调用来接受后续的传入连接。
    2、close()可用于从另一个线程中止此调用。
    3、发生错误时,例如该调用被中止、超时,将抛出IOException异常。

    (3)关闭

    方法定义:

    /**
     * 作用:
     *	关闭该监听服务端口,并释放所有关联的资源。
     * 参数:
     *	无
     * 返回:
     *	无
     */
    public void close();
    

    使用说明:

    1、该方法将导致其他线程在此套接字上阻塞的调用立即引发IOException异常。
    2、关闭这个端口不会关闭accept()方法返回的BluetoothSocket对象。
    3、该方法调用出现问题,将抛出IOException异常。


    4、BluetoothSocket类

      BluetoothSocket是蓝牙套接口。在服务器端,使用BluetoothServerSocket创建侦听服务器套接字。当连接被BluetoothServerSocket接受时,它将返回一个新的BluetoothSocket来管理连接。 在客户端,使用单个BluetoothSocket来启动连接并管理连接。最常见的蓝牙套接字类型是RFCOMM,它是Android API支持的类型。 RFCOMM是一种通过蓝牙实现的面向连接的流媒体传输。它也被称为串口行为规范(SPP)。BluetoothSocket是线程安全的,close方法始终会立即中止正在进行的操作并关闭套接字。需要BLUETOOTH权限。

    (1)连接

    方法定义:

    /**
     * 作用:
     *	尝试连接到远程蓝牙服务器。
     * 参数:
     *	无
     * 返回:
     *	无
     */
    public void connect();
    

    使用说明:

    1、此方法将阻塞,直到建立连接或连接失败。 如果此方法没有异常返回,则此套接字现在已连接。
    2、该方法调用出现问题时,例如连接失败,将抛出IOException异常。

    (2)是否连接

    方法定义:

    /**
     * 作用:
     *	获取此套接字的连接状态,即是否与远程蓝牙服务连接。
     * 参数:
     *	无
     * 返回:
     *	若连接则返回true,否则返回false。
     */
    public boolean isConnected();
    

    使用说明:

    (3)获取远程蓝牙设备

    方法定义:

    /**
     * 作用:
     *	获取此套接字连接的远程蓝牙设备。
     * 参数:
     *	无
     * 返回:
     *	连接的远程蓝牙设备BluetoothDevice对象。
     */
    public BluetoothDevice getRemoteDevice();
    

    使用说明:

    (4)获取输入流

    方法定义:

    /**
     * 作用:
     *	获取与此套接字关联的输入流。
     * 参数:
     *	无
     * 返回:
     *	输入流对象。
     */
    public InputStream getInputStream();
    

    使用说明:

    1、即使套接字尚未连接,输入流也会返回,但对该流的操作将抛出IOException异常,直到关联的套接字连接。
    2、该方法调用出错时,将抛出IOException异常。
    3、通过此方法获取的输入流对象,可以读取对端发送的数据。

    (5)获取输出流

    方法定义:

    /**
     * 作用:
     *	获取与此套接字关联的输出流。
     * 参数:
     *	无
     * 返回:
     *	输出流对象。
     */
    public OutputStream getOutputStream();
    

    使用说明:

    1、即使套接字尚未连接,输出流也会返回,但对该流的操作将抛出IOException异常,直到关联的套接字连接。
    2、该方法调用出错时,将抛出IOException异常。
    3、通过此方法获取的输出流对象,可以发送数据给对端。

    (6)关闭

    方法定义:

    /**
     * 作用:
     *	关闭此流并释放与其关联的所有系统资源。如果流已经关闭,则调用此方法不起作用。
     * 参数:
     *	无
     * 返回:
     *	无
     */
    public void close();
    

    使用说明:

    1、该方法调用出现问题,将抛出IOException异常。


    三、经典蓝牙开发流程

    1、经典蓝牙开发流程分析

    在这里插入图片描述

    2、蓝牙服务端实现

    (1)在工程清单文件AndroidManifest.xml中添加权限:
    <!--如果使用了BLUETOOTH_ADMIN权限,那么必须使用BLUETOOTH权限-->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    
    <!--android6.0后需要搜索周边蓝牙设备,需要添加以下两个权限-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    
    <!--要求设备硬件必须支持蓝牙-->
    <uses-feature android:name="android.hardware.bluetooth" android:required="true"/>
    
    (2)获取本地蓝牙适配器:
    BluetoothAdapter mAdapter= BluetoothAdapter.getDefaultAdapter();
    
    (3)打开蓝牙:
    //方式一:通过Intent来向用户弹框请求打开蓝牙,可以重写onActivityResult来监听打开蓝牙的请求结果
    //打开蓝牙
    public void openBluetooth(){
        if(mBluetoothAdapter==null){
    		//自定义方法,用来往TextView上添加提示信息
            showTip("当前设备不支持蓝牙功能!");
            return;
        }
    
        if(mBluetoothAdapter.isEnabled()){
            showTip("蓝牙已打开");
            return;
        }
    
        Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(intent,GlobalDef.REQ_CODE_OPEN_BT);
    }
    
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode==GlobalDef.REQ_CODE_OPEN_BT){
            if(resultCode == Activity.RESULT_OK){
                showTip("蓝牙打开成功");
            }
            else{
                showTip("蓝牙打开失败");
            }
        }
    }
    
    //方式二:通过enable方法静默打开蓝牙,无需用户同意(部分Android系统使用该方法依然会弹框提示,向用户请求打开蓝牙)
    mBluetoothAdapter.enable();
    
    (4)关闭蓝牙
    //关闭蓝牙,无需用户同意(部分Android系统使用该方法依然会弹框提示)
    mBluetoothAdapter.disable();
    
    (5)允许蓝牙可见:
    //方式一:通过Intent方式向用户请求允许蓝牙被搜索
    //注:如果蓝牙没有开启,用户点击确定后,会首先开启蓝牙,继而设置蓝牙能被扫描
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    //设置蓝牙可见性的时间,默认持续时间为120秒,每个请求的最长持续时间上限为300秒
    intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
    startActivity(intent);
    
    //方式二:通过反射的方式来设置蓝牙可见性,且不会出现弹框
    //注:如果蓝牙没有开启,通过此方式并不会直接打开蓝牙
    /**
     * 设置蓝牙可见
     * @param adapter
     * @param timeout 超时为0时,永久可见
     */
    public static void setDiscoverableTimeout(BluetoothAdapter adapter, int timeout) {
        //BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
        try {
            Method setDiscoverableTimeout = BluetoothAdapter.class.getMethod("setDiscoverableTimeout", int.class);
            setDiscoverableTimeout.setAccessible(true);
            Method setScanMode =BluetoothAdapter.class.getMethod("setScanMode", int.class,int.class);
            setScanMode.setAccessible(true);
    
            setDiscoverableTimeout.invoke(adapter, timeout);
            setScanMode.invoke(adapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,timeout);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 关闭蓝牙可见
     * @param adapter
     */
    public static void closeDiscoverableTimeout(BluetoothAdapter adapter) {
        try {
            Method setDiscoverableTimeout = BluetoothAdapter.class.getMethod("setDiscoverableTimeout", int.class);
            setDiscoverableTimeout.setAccessible(true);
            Method setScanMode =BluetoothAdapter.class.getMethod("setScanMode", int.class,int.class);
            setScanMode.setAccessible(true);
    
            setDiscoverableTimeout.invoke(adapter, 1);
            setScanMode.invoke(adapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE,1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    (6)创建蓝牙服务套接字,等待其他蓝牙客户端连接:
    try{
    	mSocketList=new LinkedList<BluetoothSocket>();//用来管理连接的蓝牙套接字
        mExecutorService= Executors.newCachedThreadPool();//创建线程池
    	//创建蓝牙服务端
        mServerSocket=mBluetoothAdapter.listenUsingRfcommWithServiceRecord("BluetoothTool", GlobalDef.BT_UUID);
        mServerRunningFlag=true;
    
        showTip("蓝牙服务端成功启动");
        new Thread(){
    		@Override
            public void run(){
    			try{
    				BluetoothSocket socket=null;
    				//循环等待蓝牙socket连接
                    while(mServerRunningFlag){
    					socket=mServerSocket.accept();//阻塞式
                        mSocketList.add(socket);
    					//SocketThread为自定义的线程类,用于管理BluetoothSocket的读写操作
                        mExecutorService.execute(new SocketThread(socket));
                    }
    			}catch (Exception e){
                    e.printStackTrace();
                }
            }
        }.start();
    }catch(IOException e){
        e.printStackTrace();
        showTip("服务端启动出现异常");
        Log.e(TAG,"runServer IOException");
    }
    
    (7)连接成功之后,就通过获取BluetoothSocket的输入输出流进行数据传输:
    // 获取流
    InputStream inputStream = socket.getInputStream();
    OutputStream outputStream = socket.getOutputStream();
    // 写出、读入
    byte[] temp=new byte[1024];
    inputStream.read(temp);//当无数据时将阻塞等待
    outputStream.write(temp);
    
    (8)以下为用于操作BluetoothSocket的SocketThread的简单实现,仅供参考:
    class SocketThread extends Thread {
        private BluetoothSocket mSocket=null;
        private InputStream mIn;
        private OutputStream mOut;
        private boolean isOpen = false;
    
        public SocketThread(BluetoothSocket socket) {
            try {
                mSocket=socket;
                mIn = mSocket.getInputStream();
                mOut = mSocket.getOutputStream();
                isOpen = true;
                Log.d(TAG, "a socket thread create");
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, "create SocketThread fail");
            }
        }
    
        @Override
        public void run() {
            int readLen=0;
            byte[] buffer=new byte[1024];
            try{
                while(isOpen){
                    readLen=mIn.read(buffer);
                    if(readLen>0){
                        Log.i(TAG,"read data length="+readLen);
    					Log.i(TAG,"read data hex = "+StringUtil.bytesToHexString(buffer,0,readLen));
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
                release();
            }
        }
    
        /**
         * 写入数据
         * @param data
         * @param offset
         * @param len
         */
        public void writeData(byte[] data,int offset,int len){
            if (data == null || offset<0 || len<=0 || (len+offset)>data.length) {
                Log.e(TAG,"BT writeData params fail");
                return;
            }
    
            try {
    			mOut.write(data,offset,len);
                mOut.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    	public void release(){
            Log.d(TAG,"A socketThread release");
            try{
                isOpen=false;
    
                if(mOut!=null){
                    try{
                        mOut.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    mOut=null;
                }
                if(mIn!=null){
                    try{
                        mIn.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    mIn=null;
                }
                if(mSocket!=null){
                    try{
                        mSocket.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    mSocket=null;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

    3、蓝牙客户端实现

    (1)添加权限,同蓝牙服务端。
    (2)获取本地蓝牙适配器,同蓝牙服务端。
    (3)打开蓝牙,同蓝牙服务端。
    (4)关闭蓝牙,同蓝牙服务端。
    (5)允许蓝牙可见,同蓝牙服务端。
    (6)定义蓝牙广播接收器,用于接收蓝牙搜索、连接状态改变等的广播:
    class BluetoothBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){
            String action=intent.getAction();
            Log.d(TAG,"Action received is "+action);
            //蓝牙搜索
            if(BluetoothDevice.ACTION_FOUND.equals(action)){
                BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if(scanDevice == null || scanDevice.getName() == null){
                    return;
                }
    
                int btType=scanDevice.getType();
                if(btType==BluetoothDevice.DEVICE_TYPE_LE || btType==BluetoothDevice.DEVICE_TYPE_UNKNOWN){
                    return;
                }
    
                Log.d(TAG, "bt name="+scanDevice.getName()+" address="+scanDevice.getAddress());
    			//将搜索到的蓝牙设备加入列表
                deviceList.add(scanDevice);
                short rssi=intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);
                rssiList.add(rssi);
                listAdapter.notifyDataSetChanged();//通知ListView适配器更新
            }
            //蓝牙配对
            else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if(mCurDevice!=null && btDevice.getAddress().equals(mCurDevice.getAddress())){
                    int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
                    if(state==BluetoothDevice.BOND_NONE){
                        showTip("已取消与设备" + btDevice.getName() + "的配对");
                        mFlag=-1;
                    }
                    else if(state==BluetoothDevice.BOND_BONDED){
                        showTip("与设备" + btDevice.getName() + "配对成功");
                        mFlag=1;
                    }
                }
            }
            else if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
                int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
                switch (blueState) {
                    case BluetoothAdapter.STATE_TURNING_ON:
                        Log.i(TAG,"onReceive---------STATE_TURNING_ON");
                        break;
                    case BluetoothAdapter.STATE_ON:
                        Log.i(TAG,"onReceive---------STATE_ON");
                        showTip("蓝牙当前状态:ON");
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        Log.i(TAG,"onReceive---------STATE_TURNING_OFF");
                        break;
                    case BluetoothAdapter.STATE_OFF:
                        Log.i(TAG,"onReceive---------STATE_OFF");
                        showTip("蓝牙当前状态:OFF");
                        break;
                }
            }
        }
    }
    
    (7)注册广播:
        mBluetoothBroadcastReceiver=new BluetoothBroadcastReceiver();
        IntentFilter filter=new IntentFilter();
        filter.addAction(BluetoothDevice.ACTION_FOUND);
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        mContext.registerReceiver(mBluetoothBroadcastReceiver,filter);
    
    (8)搜索周边蓝牙设备:
        if(mBluetoothAdapter.isDiscovering()){
            mBluetoothAdapter.cancelDiscovery();
        }
    	//搜索到的蓝牙设备通过广播接收
        mBluetoothAdapter.startDiscovery();
    
    (9)建立与蓝牙服务器的连接:
    /**
     * 蓝牙配对并连接
     */
    public void bondAndConnect(BluetoothDevice mCurDevice){
        //取消搜索
        if(mBluetoothAdapter.isDiscovering()){
            mBluetoothAdapter.cancelDiscovery();
        }
    
        if(mCurDevice==null){
            showTip("远程蓝牙设备为空!");
            return;
        }
    
        //当前蓝牙设备未配对,则先进行配对
        if(mCurDevice.getBondState()==BluetoothDevice.BOND_NONE){
            Log.d(TAG,"create bond to "+mCurDevice.getName());
            boolean nRet= BluetoothUtil.createBond(mCurDevice);
            if(!nRet){
                showTip("createBond fail!");
                return;
            }
            showLoadingDialog("正在与【"+mCurDevice.getName()+"】进行配对...");
            mFlag=0;
            while(mFlag==0){
                SystemClock.sleep(250);
            }
            if(mFlag==-1){
                showTip("与【"+mCurDevice.getName()+"】的蓝牙配对失败");
                dismissLoadingDialog();
                return;
            }
        }
    
        if(mCurDevice.getBondState()==BluetoothDevice.BOND_BONDED){
            showLoadingDialog("正在与【"+mCurDevice.getName()+"】进行连接...");
            try {
                //创建Socket
                BluetoothSocket socket = mCurDevice.createRfcommSocketToServiceRecord(GlobalDef.BT_UUID);
                //连接蓝牙服务套接字
                socket.connect();
                mThread=new SocketThread(socket);
                mThread.start();
                showTip(("成功与【"+mCurDevice.getName()+"】建立连接"));
            } catch (IOException e) {
                Log.d(TAG,"socket connect fail");
                showTip(("连接【"+mCurDevice.getName()+"】失败"));
                e.printStackTrace();
            }
        }
        dismissLoadingDialog();
    }
    
    (10)连接成功之后,就通过输入输出流进行数据传输,同蓝牙服务端。

    四、注意事项与常见问题

    1、Android6.0以后,搜索蓝牙设备需要位置权限(危险权限,需要动态申请)。
    2、高版本Android系统上进行蓝牙搜索时,除了动态申请位置权限外,有的可能还需要手动打开设备的位置信息,否则无法搜索蓝牙。

    在这里插入图片描述

    3、搜索周边蓝牙设备时,本机并不需要处于蓝牙可见状态。但其他设备必须处于蓝牙可见状态,本机才可以搜索到。已知蓝牙设备(处于有效范围内)的MAC地址,则随时可以向其发起连接,而无需打开蓝牙可见性。
    4、如果尚未在设备上启用蓝牙,则启用设备可检测性将会自动启用蓝牙。
    5、搜索周边设备对于蓝牙适配器而言是一个非常繁重的操作过程,并且会消耗大量资源。在找到要连接的设备后,确保始终使用 cancelDiscovery() 停止发现,然后再尝试连接。 此外,如果已经保持与某台设备的连接,那么执行搜索操作可能会大幅减少可用于该连接的带宽,因此不应该在处于连接状态时执行搜索周边蓝牙的操作。
    6、在调用 connect() 时,应始终确保设备未在进行搜索操作。 如果正在进行搜索操作,则会大幅降低连接尝试的速度,并增加连接失败的可能性。
    7、搜索周边设备,通过广播能够获取到周边蓝牙设备的名称、mac地址、rssi信号强度等。
    8、BluetoothSocket是线程安全的, close()方法会终止BluetoothSocket进行的一切操作, 并且同时会关闭连接。
    9、经典蓝牙是通过流来进行数据收发的,对流进行数据读取操作时,由于接收方不知道消息之间的界限,不知道一次性提取多少字节的数据,因而容易产生数据粘包问题。对于这种问题,可以给每个完整的数据包添加一个起始符与结束符,那么接收方就可以确定需要读取并处理的数据范围。



    具体的演示demo放在Github上,感兴趣的同学可以了解一下。如果有什么问题,欢迎来一起讨论,共同进步。

    GitHub地址:https://github.com/JINSHENGCCC/Android_Common/tree/master/Android-BT/src


    五、附录

    1. 深入了解Android蓝牙Bluetooth——《基础篇》
    2. Android 经典蓝牙开发
    3. Android蓝牙开发—经典蓝牙详细开发流程
    4. 蓝牙概览
    更多相关内容
  • 我的博客“Android 蓝牙开发,蓝牙连打印机”有环境可运行截图。希望对大家有帮助,放心用吧。
  • 本资源介绍了蓝牙开发基础机使用BlueLab进行开发,是入门级教程资料 本资源介绍了蓝牙开发基础机使用BlueLab进行开发,是入门级教程资料
  • 低功耗蓝牙开发权威指南通过与经典蓝牙作对比,详尽介绍了将深入影响下一次无线技术革命的低功耗蓝牙技术的产生、设计、工作原理及其良好的节能、抗干扰特性和灵活、简单的开发特点。本书分为四个部分,分别阐述了低...
  • 主要为大家详细介绍了iOS蓝牙开发数据实时传输,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 蓝牙 杰理 AC692X SDK介绍 手册 有需要的可以下载查看
  • 此压缩包包含两个jar包,bluecove-2.1.1-SNAPSHOT.jar和commons-io-2.6,是java蓝牙通讯所需的jar,bluecove,官方提供的jar包只支持32位,此jar包经过某大神改良,可在64位电脑上使用
  • java蓝牙开发demo

    2018-03-18 14:33:08
    java蓝牙开发demo包含所需jar,电脑需具有内置蓝牙或外置蓝牙模块
  • 最近公司开发需要用到蓝牙,花了大约一天的时间总结整理了一下。主要是为了以后自己方便看。有需要的朋友可以看下。欢迎一起讨论。后面会带上博客。里面是实现了蓝牙搜索,配对,连接,数据互传。
  • 这个demo实现了Android蓝牙开发,创建客户端,服务端。并实现互发消息,接收消息
  • 先回顾一下上一篇文章《Android蓝牙开发系列文章-蓝牙音箱连接》讲到的蓝牙音箱的完成配对、连接的流程:扫描设备–监听DEVICE_FOUND广播–>直到找到目标设备–>对目标设备发起配对–>监听到设备配对成功–>发起设备...
  • 安卓蓝牙开发视频教程Android Bluetooth 视频教程安卓视频教程
  • 蓝牙开发demo

    2019-03-20 11:52:11
    这是一个蓝牙基本功能的例子,运行在Android studio上。有打开蓝牙,关闭蓝牙,搜索蓝牙,配对蓝牙等等...
  • 我们在《Android蓝牙开发系列文章-策划篇》中计划讲解一下蓝牙BLE,现在开始第一篇:Android蓝牙开发系列文章-玩转BLE开发(一)。计划要写的BLE文章至少分四篇,其他三篇分别讲解:BLE Server端编码(用手机模拟...
  • Android BLE 蓝牙开发框架,使用回调方式处理,搜索、连接、notify、indicate、读、写等一系列蓝牙操作
  • B站Android蓝牙开发学习(Rex老师)视频讲座的的最终源码,BlueToothClass6,已适配最新AS。
  • android booth - 蓝牙开发框架
  • QT开发蓝牙串口在安卓上面运行,实现一个简单的类似于串口助手一样的功能。 具体介绍请参考我的博客:http://www.cnblogs.com/sigma0/p/5769527.html
  • 那么在Android手机中怎样进行蓝牙开发呢?本文以实例的方式讲解Android蓝牙开发的知识。  1、使用蓝牙的响应权限 XML/HTML代码 <uses android:name=android.permission.BLUETOOTH_ADMIN/>  2、配置本机蓝牙模块...
  • Android蓝牙开发 — 经典蓝牙&BLE蓝牙

    千次阅读 2021-04-30 20:02:10
    一,前期基础知识储备 1)蓝牙是一种支持设备之间短距离通信的无线电技术(其他还包括红外,WIFI); 支持移动电话、笔记本电脑、...2)蓝牙开发技术一共分为两种: 经典蓝牙,3.0版本以下的蓝牙,功耗高,传输

    一,前期基础知识储备

    1)蓝牙是一种支持设备之间短距离通信的无线电技术(其他还包括红外,WIFI);

    支持移动电话、笔记本电脑、无线耳机等设备之间进行信息的交换;

    Android支持的蓝牙协议栈:Bluz,BlueDroid,BLE

    • Bluz是Linux推出的,目前使用最广泛;
    • BlueDroid是Android4.0之后推出来的,简化了Bluz的操作;
    • BLE是最新的低功耗协议,传输效率和传输速率都是很高的;

    2)蓝牙开发技术一共分为两种:

    • 经典蓝牙,3.0版本以下的蓝牙,功耗高,传输数据量大,有效距离10米;
    • 低功耗蓝牙BLE,4.0及以上版本,低功耗,数据量小,有效距离40米;

    经典蓝牙的开发包括蓝牙的互相连接、读取蓝牙列表、文件传输、蓝牙耳机等等;

    特点:基于Socket连接,传输速率快;缺点:耗电,距离短;
    通信的流程,发现设备 -> 配对/绑定设备  ->  建立连接  ->  数据通信  ->  断开连接

    BLE蓝牙开发主要是低功耗设备(临近设备间传输少量数据),比如血糖仪、蓝牙手环、蓝牙手表、蓝牙温度枪等等;Android 4.3(API 级别 18)为发挥核心作用的蓝牙低功耗 (BLE) 引入内置平台支持,并提供相应 API,方便应用发现设备、查询服务和传输信息。与传统蓝牙不同,蓝牙低功耗 (BLE) 旨在提供显著降低的功耗这使 Android 应用可与功率要求更严格的 BLE 设备(例如近程传感器、心率监测仪和健身设备)通信

    注意:当用户使用 BLE 将其设备与其他设备配对时,用户设备上的所有应用都可以访问在这两个设备间传输的数据。

    因此,如果您的应用捕获敏感数据,您应实现应用层安全以保护此类数据的私密性。

    3)蓝牙通信底层原理:

    Android 平台包含蓝牙网络堆栈支持,此支持能让设备以无线方式与其他蓝牙设备交换数据。应用框架提供通过 Android Bluetooth API 访问蓝牙功能的权限。这些 API 允许应用以无线方式连接到其他蓝牙设备,从而实现点到点和多点无线功能。

    为了让支持蓝牙的设备能够在彼此之间传输数据,它们必须先通过配对过程形成通信通道。其中一台设备(可检测到的设备)需将自身设置为可接收传入的连接请求。另一台设备会使用服务发现过程找到此可检测到的设备。在可检测到的设备接受配对请求后,这两台设备会完成绑定过程,并在此期间交换安全密钥。二者会缓存这些密钥,以供日后使用。完成配对和绑定过程后,两台设备会交换信息。当会话完成时,发起配对请求的设备会发布已将其链接到可检测设备的通道。但是,这两台设备仍保持绑定状态,因此在未来的会话期间,只要二者在彼此的范围内且均未移除绑定,便可自动重新连接。

    4)关键类和接口

    android.bluetooth 包中提供所有 Bluetooth API。以下概要列出了创建蓝牙连接所需的类和接口:

     BluetoothAdapter
    表示本地蓝牙适配器(蓝牙无线装置)。BluetoothAdapter 是所有蓝牙交互的入口点。借助该类,您可以发现其他蓝牙设备、查询已绑定(已配对)设备的列表、使用已知的 MAC 地址实例化 BluetoothDevice,以及通过创建 BluetoothServerSocket 侦听来自其他设备的通信。

    <!--蓝牙连接权限-->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <!--蓝牙通讯权限-->
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    
    // 启动蓝牙
    public void turnOnBlueTooth(Activity activity, int requestCode) {
    	Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    	activity.startActivityForResult(intent, requestCode);
    	// mAdapter.enable(); // 谷歌不推荐这种方式
    }
    
    // 关闭蓝牙
    public void turnOffBluetooth() {
    	mAdapter.disable();
    }
    
    // 打开蓝牙可见性
    public void enableVisibily(Context context) {
    	Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    	intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
    	context.startActivity(intent);
    }

    BluetoothDevice
    表示远程蓝牙设备。借助该类,您可以通过 BluetoothSocket 请求与某个远程设备建立连接,或查询有关该设备的信息,例如设备的名称、地址、类和绑定状态等。

    // 查找设备
    public void findDevice() {
    	assert (mAdapter != null);
    	mAdapter.startDiscovery();
    }
    
    // 绑定设备
    public boolean createBond(BluetoothDevice device) {
    	boolean result = device.createBond();
    	return result;
    }
    
    // 绑定状态
    BluetoothDevice.BOND_BONDED
    BluetoothDevice.BOND_BONDING
    BluetoothDevice.BOND_NONE
    
    // 获取已绑定的蓝牙设备
    public List<BluetoothDevice> getBondedDeviceList() {
    	return new ArrayList<>(mAdapter.getBondedDevices());
    }
    
    // 解除绑定
    public boolean removeBond(Class btClass, BluetoothDevice btDevice)
    throws Exception {
    	Method removeBondMethod = btClass.getMethod("removeBond");
    	Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
    	return returnValue.booleanValue();
    }
    
    
    // 蓝牙操作中发出的广播
    private void registerBluetoothReceiver() {
    	IntentFilter filter = new IntentFilter();
    	//开始查找
    	filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
    	//结束查找
    	filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    	//查找设备
    	filter.addAction(BluetoothDevice.ACTION_FOUND);
    	//设备扫描可见改变 当我可以被看见时就会发送一个广播过来
    	filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
    	//绑定状态
    	filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
    
    	registerReceiver(receiver, filter);
    }

    BluetoothSocket
    表示蓝牙套接字接口(类似于 TCP Socket)。这是允许应用使用 InputStream 和 OutputStream 与其他蓝牙设备交换数据的连接点。

    close(),关闭
    connect()连接
    getInptuStream()获取输入流
    getOutputStream()获取输出流
    getRemoteDevice()获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备

    BluetoothServerSocket
    表示用于侦听传入请求的开放服务器套接字(类似于 TCP ServerSocket)。如要连接两台 Android 设备,其中一台设备必须使用此类开放一个服务器套接字。当远程蓝牙设备向此设备发出连接请求时,该设备接受连接,然后返回已连接的 BluetoothSocket。

    void    close()
        closes the object and release any system resources it holds.
    void    connect()
        attempt to connect to a remote device.
    InputStream getInputStream()
        get the input stream associated with this socket.
    OutputStream    getOutputStream()
        get the output stream associated with this socket.
    BluetoothDevice getRemoteDevice()
        get the remote device this socket is connecting, or connected, to.
    获取远程设备,该套接字连接,或连接到---。
    boolean isConnected()
        get the connection status of this socket, ie, whether there is an active connection with remote device.
    判断当前的连接状态

    BluetoothSocket 和 BluetoothServerSocket 
    类似于Java中的套接字的 Socket 和 ServerSocket;
    在服务器端和客户端进行数据传输的时候都要使用这个类;

    服务器端 : 使用BluetoothServerSocket对象可以创建一个BluetoothSocket对象, 调用BluetoothServerSocket的accept()方法就可以获取该对象;
    客户端 : 调用BluetoothDevice的createRfcommSocketToServiceRecord()可以获取该对象; 

    在服务器端BluetoothServerSocket进行accept()阻塞, 在客户端BluetoothSocket调用connect()连接服务器, 如果连接成功, 
    服务器端的accept()方法就会返回BluetoothSocket对象, 同时客户端的BluetoothSocket也成功连接服务器, 
    此时服务器端和客户端的BluetoothSocket对象就可以获取输入输出流, 对数据进行操作;


    基于蓝牙这套通信流程,可以实现一个简单的聊天程序。

    BluetoothHeadset
    提供蓝牙耳机支持,以便与手机配合使用。这包括蓝牙耳机配置文件和免提 (v1.5) 配置文件。
    BluetoothA2dp
    定义如何使用蓝牙立体声音频传输配置文件 (A2DP),通过蓝牙连接将高质量音频从一个设备流式传输至另一个设备

    • A2DP蓝牙立体声音频传输配置文件 (A2DP) 定义如何通过蓝牙连接和流式传输,将高质量音频从一个设备传输至另一个设备。Android 提供 BluetoothA2dp 类,该类是用于控制蓝牙 A2DP 服务的代理。

    官方文档:《蓝牙概览》《蓝牙低功耗概览》《BluetoothA2dp

    5)电话音频协议(HSP,HFP)和媒体音频协议(A2DP,AVRCP)

    • HSP(手机规格)– 提供手机(移动电话)与耳机之间通信所需的基本功能。
    • HFP(免提规格)– 在 HSP 的基础上增加了某些扩展功能,原来只用于从固定车载免提装置来控制移动电话。
    • A2DP(高级音频传送规格)– 允许传输立体声音频信号。 (相比用于 HSP 和 HFP 的单声道加密,质量要好得多)。
    • AVRCP(音频/视频遥控规格)–用于从控制器(如立体声耳机)向目标设备(如装有 Media Player 的电脑)发送命令(如前跳、暂停和播放)。

    关于A2DP,安卓手机都是支持A2DP的,只需要通过广播就可以获取状态了。

    关于AVRCP,这部分嵌入式工程师的逻辑多,硬件上的按键可以控制手机app的,比如按键加减时,可以与app交互。

    A2DP全名是Advanced Audio Distribution Profile,高质量音频数据传输的协议,其定义里了传送单声道或立体声等高质量音频(区别于蓝牙SCO链路上传输的普通语音)信息的协议和过程。A2DP的典型应用是将音乐播放器的音频数据发送到耳机或音箱。 

    参考文章:《Android 蓝牙开发之A2DP基本功能

    参考项目:sample-bluetooth-audio(Bluetooth A2DP sample using Android Things)

    二,上代码,具体实现

    经典蓝牙开发文章:《Android蓝牙开发—经典蓝牙详细开发流程》《android 经典蓝牙开发

    BLE蓝牙开发文章:《Android之低功耗蓝牙的基本使用》《Android Kotlin&BLE(低功耗蓝牙) 笔记

                                    《Android BLE蓝牙详细解读》《Android 蓝牙开发(三) -- 低功耗蓝牙开发

    总结:

    1)写过经典蓝牙的就知道,如果说两者的搜索操作还差不多的话,连接操作和写入操作就是完全不同的东西了。

    经典蓝牙可以获取到一个类似 TCP 中 Socket 的对象,然后获取 InputStream 和OutputStream,二者分别通过套接字以及 getInputStream()和 getOutputStream()来处理数据传输。

    而 BLE 中需要通过不同的 UUID 获取对应的服务、特征才可以写入数据。

    2)UUID:每个服务和特征都会有唯一的 UUID ,由硬件决定。
    服务(Service):蓝牙设备中可以定义多个服务,相当于功能的集合。
    特征(Characteristic):一个服务可以包含多个特征,可以通过 UUID 获取到对应的特征的实例,通过这个实例就可以向蓝牙设备发送 / 读取数据。

    蓝牙开源框架:《一款适用经典蓝牙的快速开发框架》《开源蓝牙框架 Android-BLE

    三,蓝牙5.0

    06wj jsdt ly 01

    古老无线再升级 深入了解蓝牙5.0技术

    查看源图像

    蓝牙5.0是由蓝牙技术联盟在2016年提出的蓝牙技术标准,蓝牙5.0针对低功耗设备速度有相应提升和优化,蓝牙5.0结合wifi对室内位置进行辅助定位,提高传输速度,增加有效工作距离。上一次蓝牙4.2是公布于2014年12月。

    补充一点内容

    划时代的蓝牙 4.0 

    蓝牙技术联盟(Bluetooth SIG)在2010年发布了跨时代的蓝牙4.0,它并不是蓝牙3.0的简单升级版本,
    而是全新的技术架构,蓝牙4.0版本分两种模式:单模蓝牙和双模蓝牙。常见的蓝牙音箱,是典型的双模蓝牙,它需要传输大量的音频数据。
    而小米手环,蓝牙温度计则属于单模蓝牙。行业里一般不讲单模蓝牙,而是统一称为低功耗蓝牙。

    相对于经典蓝牙,低功耗蓝牙芯片有传输远、功耗低、延迟低等优势。传输距离方面,经典蓝牙只有10-100米,而BLE最远能传输300米;
    连接方式上,经典蓝牙只能通过点对点的方式传输,而BLE设备能够能通过点对点、广播、Mesh组网与其他设备相连;
    在功耗上两者的差别巨大,低功耗蓝牙运行和待机功耗极低,使用一颗纽扣电池便能连续工作数月甚至数年之久。

    经典蓝牙主要用于大量音频传输的情景,而低功耗蓝牙主要用在非音频数据传输上。
    基于这个差距,经典蓝牙和低功耗蓝牙应用场景有所不同。经典蓝牙主要应用在音频传输设备上, 
    而低功耗蓝牙主要用在数据传输领域,尤其是以物联网为主的数据传输。
    如血糖仪、蓝牙手环、蓝牙手表、蓝牙温度枪、近程传感器、心率监测仪和健身设备等等。

     蓝牙音频传输 - 手机端的音乐如何通过转码的方式传输到蓝牙耳机进行播放。

    参考文章:

    经典蓝牙与低功耗蓝牙芯片功能性能对比

    BLE技术揭秘

    说说蓝牙音频常用的编解码格式

    Audio/Video Remote Control Profile (AVRCP)

    展开全文
  • Android5.0蓝牙开发

    2019-03-12 13:39:35
    这是一个用于帮助开发者更好的了解封装包的API,以便于新手更快,更准的上手
  • 本文档翻译了QT蓝牙章节的一些技术资料,开发蓝牙必备。经过实际开发测试,QT5的蓝牙API可以直接操作Android手机的蓝牙,可以实现基本蓝牙通信,比如 实现手机与HC-05蓝牙通信等。
  • 微信小程序 蓝牙开发

    2018-11-23 16:44:58
    微信小程序蓝牙功能开发,支持安卓和苹果,需要的小同学拿走
  • Android蓝牙开发

    2017-03-26 20:39:11
    Android蓝牙开发
  • 《低功耗蓝牙开发权威指南》通过与经典蓝牙作对比,详尽介绍了将深入影响下一次无线技术革命的低功耗蓝牙技术的产生、设计、工作原理及其良好的节能、抗干扰特性和灵活、简单的开发特点。本书分为四个部分,分别阐述...
  • C#蓝牙开发SDK内附有文档-可用于开发蓝牙通讯协议,内附有详细文档
  • 最近,随着智能穿戴式设备、智能医疗以及智能家居的普及,蓝牙开发在移动开中显得非常的重要。由于公司需要,研究了一下,蓝牙4.0在Android中的应用。 以下是我的一些总结。 1.先介绍一下关于蓝牙4.0中的一些名词吧...
  • TI蓝牙开发手册

    2014-05-04 22:11:16
    TI蓝牙开发手册,CC2541 SensorTag Quick Start Guide

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 67,028
精华内容 26,811
关键字:

蓝牙开发

友情链接: l5972d.rar