Android开发经验

Android 蓝牙开发(一)

2021-11-23  本文已影响0人  因为我的心

一、前言:

普通蓝牙设备官方文档

Android 平台包含蓝牙网络堆栈支持,凭借此支持,设备能以无线方式与其他蓝牙设备交换数据。应用框架提供了通过 Android Bluetooth API 访问蓝牙功能的途径。使用 Bluetooth API Android 应用可以执行下面的操作:

传统蓝牙适用于电池使用强度较大的操作,例如 Android 设备之间的流传输和通信等。针对具有低功耗要求的蓝牙设备,Android 4.3(API 18)中引入了面向低功耗蓝牙的 API 支持。

二、名词介绍:

使用 Android Bluetooth API 来完成使用蓝牙进行通信的四项主要任务:设置蓝牙查找局部区域内的配对设备或可用设备连接设备,以及在设备之间传输数据

关于蓝牙的 API 在 android.bluetooth 包中,下面介绍一下和蓝牙相关的主要类:

1、BluetoothAdapter

本地蓝牙适配器,是所有蓝牙交互的入口点,表示蓝牙设备自身的一个蓝牙设备适配器,整个系统只有一个蓝牙适配器。通过它可以发现其他蓝牙设备,查询绑定(配对)设备的列表,使用已知的 Mac 地址实例化 BluetoothDevice 以及创建 BluetoothServerSocket 用来侦听来自其他设备的通信。

2、BluetoothDevice

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

3、BluetoothSocket

表示蓝牙套接字接口(与 TCP Socket 相似)。这是允许应用通过 InputStreamOutputStream 与其他蓝牙设备交换数据的节点。正是利用这个对象来完成蓝牙设备间的数据交换,

4、BluetoothServerSocket

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

5、BluetoothClass

描述蓝牙设备的一般特性和功能。这是一组只读属性,用于定义设备的主要和次要设备类及其服务。不过,它不能可靠地描述设备支持的所有蓝牙配置文件和服务,而是适合作为设备类型提示。

6、BluetoothProfile

表示蓝牙配置文件的接口。蓝牙配置文件是适用于设备间蓝牙通信的无线接口规范。免提配置文件便是一个示例。如需了解关于配置文件的详细讨论,参考下面配置文件的讲解

7、BluetoothHeadset

提供蓝牙耳机支持,以便与手机配合使用。其中包括蓝牙耳机和免提(1.5版)配置文件。BluetoothProfile 的实现类

8、BlutoothA2dp

定义高质量音频如何通过蓝牙连接和流式传输,从一台设备传输到另一台设备。“A2DP”代表高级音频分发配置文件。是 BluetoothProfile的实现类

9、BluetoothHealth

表示用于控制蓝牙服务的健康设备配置文件代理。BluetoothProfile 的实现类。

10、BluetoothGatt

BluetoothProfile 的实现类。与低功耗蓝牙通信有关的配置文件代理

11、BluetoothHealthCallback

用于实现 BluetoothHealth 回调的抽象类。必须扩展此类并实现回调方法,以接收关于应用注册状态和蓝牙通道状态变化的更新内容。

12、BluetoothHealthAppConfiguration

表示第三方蓝牙健康应用注册的应用配置,以便与远程蓝牙健康设备通信

13、BluetoothProfile.ServiceListener

BluetoothProfile IPC 客户端连接到服务(即,运行特定配置文件的内部服务)或断开服务连接时向其发送通知的接口。

三、基本使用:

1、蓝牙权限

使用蓝牙必须声明权限 BLUETOOTH 才可以执行蓝牙通信。

<mainifest>
    <uses-permission android:name = "android.permission.BLUETOOTH"/>
    <!--启用应用启动设备发现或者操作蓝牙设备的超级管理员-->
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
</mainifest>

2、设置蓝牙

1、获取蓝牙适配器

所有的蓝牙 Activity 都是需要 BluetoothAdapter 的。获取 BluetoothAdapter 调用 BluetoothAdapter的静态方法 getDefaultAdapter()方法。会返回一个表示设备自身的蓝牙适配器(蓝牙无线装置)的 BluetoothAdapter。
整个系统有一个蓝牙适配器,我们的应用可以通过 BluetoothAdapter 这个对象与之交互。如果 getDefaultAdapter()返回 null 则说明该设备不支持蓝牙

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if(mBluetoothAdapter == null){
        // 说明此设备不支持蓝牙操作
    }

3、启用蓝牙

需要确认是否已经开启蓝牙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_STATEEXTRA_PREVIOUS_STATE 分别表示新的和旧的蓝牙状态。

4、查找设备

5、查询配对的设备

在执行设备发现之前,有必要查询已配对的设备集合。用来了解设备是否处于已知状态。通过 getBondedDevices() 来实现,这将返回表示已配对设备的一组 BluetoothDevice

例如:我们可以查询所有已配对的设备,然后使用 ArrayAdapter 向用户显示每台设备的名称:

Set<BluetoothDevice> pairedDevices = mBlutooothAdapter.getBondedDevices();

    if(pairedDevices.size() > 0){
        for(BluetoothDevice device:pairedDevices){
            // 把名字和地址取出来添加到适配器中
            mArrayAdapter.add(device.getName()+"\n"+ device.getAddress());
        }
    }

要发起连接仅需要知道目标蓝牙设备的 Mac 地址就可以了。

6、发现设备

发现设备使用 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() 来停止发现,然后尝试连接。如果您已经和某台设备进行连接,那么这个时候执行发现操作会大幅度的减少此连接可用的带宽!因此不应该在处于连接状态的时候执行发现操作!

7、启用可检测性

如果我们希望我们的设备是可以被其他设备检测到的,可以使用 ACTION_REQUEST_DISCOVERABLE 来操作 Intent 调用 startActivityForResult(Intent,int)。这样会通过系统设置发出启用可检测到模式的请求(无需停止我们的应用)。默认情况下,设备会变为可检测状态并且持续 120 秒钟。我们还可以通过 EXTRA_DISCOVERABLE_DURATIONIntent 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(未处于可检测到模式并且无法连接)

8、连接设备

要在两台设备上的应用之间创建连接,必须同时实现服务端和客户端机制,因为其中一台设备必须开放服务器套接字,而另一台设备必须发起连接(使用服务器设备的 MAC 地址发起连接)。当服务器和客户端在同一 RFCOMM 通道上分别拥有已连接的 BluetoothSocket 时,二者将被视为彼此连接。在这种情况下每台设备都能获得输入和输出流式传输,并且可以开始传输数据。

服务端和客户端分别以不同的方式来获得 BluetoothSocket 。服务器将在传入连接被接受时收到套接字。客户端将在其打开到服务器的 RFCOMM 通道时收到该套接字。

一种实现方式是自动将每台设备准备为一个服务器,从而使每台设备开发一个服务器套接字并侦听连接。然后任一设备可以发起与另一台设备的连接,并成为客户端。或者其中一台设备可显示“托管”连接并按需开放一个服务器套接字,从而另一台设备则直接发起连接。

在连接之前如果两个设备没有配对,则系统会自动发出配对请求。

9、连接为服务器

当连接两台设备时,其中一台必须保持开发的 BluetoothServerSocket 来充当服务器,用于监听传入的连接请求,在接受了请求后提供一个已经连接的 BluetoothSocket。从 BluetoothServerSocket 连接获取 BluetoothSocket 后就可以调用 close 来关闭这个等待了。

10、关于 UUID

通用唯一标识符(UUID),用于表示唯一标识信息的字符串ID,128位。可以使用网络上众多的随机 UUID 生成器,然后通过 formString(String)来初始化一个 UUID。

服务器套接字接受连接的基本过程

字符串是我们自己定义的服务的可识别名称,可以直接使用包名。系统会自定将其写入到设备上的新服务发现协议(SDP)数据库条目中。UUID 也在 SDP 中,作为与客户端设备连接协议的匹配规则。只有客户端和这里的UUID 一样了才可以会被连接

阻塞调用,将在连接被接受或者发生异常的时候返回,操作成功后,会返回 BluetoothSocket

这样会释放服务器套接字及其所有资源,但不会关闭已经连接的 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();
    }
}

11、连接为客户端

要想和保持开发服务器套接字的设备建立连接,必须首先要获取该设备的 BluetoothDevice 对象。然后使用这个对象来获取 BluetoothSocket 并发起连接。

客户端连接的基本过程

调用 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){
                return;
            }
        }
        // 自定义方法
        manageConnectedSocket(mmSocket);

    }

    public void cancle(){
        try{
            mSocket.close();
        }cathc(IOException closeException){

        }
    }
}

在连接之前调用 cancleDiscovery() 在进行连接之前应该始终调用这个方法,而且调用的时候无需检测是否正在扫描。

12、管理连接

建立连接后的两个设备都有一个 BluetoothSocket 通过这个 Socket 就可以在这两个设备间传输数据了。

过程:

13、使用配置文件

从 Android 3.0 开始, Bluetooth API 便支持使用蓝牙配置文件。蓝牙配置文件是适用于设备间蓝牙通信的无线接口规范。

1、蓝牙配置文件就是设备间通信(蓝牙设备)的一种规范

免提配置文件便是一个示例,对于连接到无线耳机的手机,两台设备都必须支持免提配置文件。我们也可以通过实现接口 BluetoothProfile 来写入自己的类来支持特定的蓝牙配置文件。Android API 提供了以下的几种蓝牙配置文件的实现:

2、使用配置文件的基本步骤

BluetoothHeadset mBluetoothHeadset;
// 获取默认蓝牙适配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 设置监听(监听连接状态)
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener(){
    public void onServiceConnected(int profile,BluetoothProfile proxy){
        if(profile == BluetoothProfile.HEADSET){
            mBluetoothHeadset = (BluetoothHeadset)
        }
    }
    public void onServiceDisconnected(int profile){
        if(profile == BluetoothProfile.HEADSET){
            mBluetoothHeadset = null;
        }
    }
}

// 建立与配置文件代理的连接
mBluetoothAdapter.getProfileProxy(contenxt,mProfileListener,BluetoothProfile.HEADSET);
// 使用 mBluetoothHeadset 代理内部的方法

// 使用完毕后关闭
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);

14、供应商特定的 AT 命令

从 Android 3.0 开始。应用可以注册接受耳机所发送的预定义的供应商特定 AT 命令的系统广播(例如 Plantronics +XEVENT命令)(也就是说我们的应用可以接受耳机蓝牙商预定义的命令)。如:应用可以接受指示所连接设备的电池电量的广播,并根据需要通知用户或采取其他操作。使用 ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent 创建广播接收器,用来处理耳机供应商特定的 AT 命令。

15、健康设备配置文件

Android 4.0 引入了对蓝牙健康设备配置文件(HDP)的支持。这可以使用我们的应用使用蓝牙与支持蓝牙功能的健康设备进行通信(心率检测仪、血糖仪、温度计、台秤)

创建 HDP 应用:

四、总结:

关于普通蓝牙设备和普通蓝牙设备之间的连接通信

关于蓝牙设备和蓝牙仪器(蓝牙耳机、电子秤等等类似产品)
这种之间的通信是通过配置文件代理来实现的。
都有一个对应的配置文件代理类。具体的操作是通过这个对象来完成。


参考:https://mp.weixin.qq.com/s?__biz=MzU5NzA2NjQzMg==&mid=2247484128&idx=1&sn=9d59b9ad66fb68b9569c29566103c009&scene=21#wechat_redirect

上一篇下一篇

猜你喜欢

热点阅读