美文网首页
Android 蓝牙开发实践笔记

Android 蓝牙开发实践笔记

作者: 留给时光吧 | 来源:发表于2018-04-23 20:15 被阅读0次

    本文基于传统蓝牙开发。

    先来梳理一下蓝牙开发的逻辑,基本分为以下几步:搜索设备,配对设备,连接设备,传输数据。前三步主要借助Android提供的蓝牙相关的API实现,最后一步是基于Socket的,类似于TCP协议传输数据,不过用的并不是普通Java类中的Socket,而是蓝牙专用的BluetoothServerSocket和BluetoothSocket,类的模型和TCP协议中的ServerSocket和Socket类似。

    首先要清楚,蓝牙设备传输之前要进行设备的发现和配对,这两步我们可以调用系统设置界面完成或者自己实现,调用系统设置代码如下:

    Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
    startActivity(intent);
    

    为了加深对开发流程的了解,我们这里自己手动实现以下开启蓝牙并搜索配对的逻辑。

    第一步要有相关权限:

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    

    前两个是蓝牙相关的,不需要动态申请,后面4个都需要动态申请。第3和第4个权限如果不申请的话是搜索不到周围设备的,最后两个是为了传输文件用的。

    蓝牙几乎所有操作都要用到BluetoothAdapter这个类,获取方法:

    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    

    1.打开关闭蓝牙与状态更新

    获取到之后,我们第一步可以判断蓝牙状态并在需要时打开,蓝牙打开关闭的代码:

    bluetoothAdapter.enable();  //打开蓝牙
    bluetoothAdapter.disable();  //关闭蓝牙
    

    有一点需要注意的时,使用enable()虽然可以打开蓝牙,但并不能保证在所有系统上都有效,因为这种方法不带任何提示,不太友好。所以还有另外一种带提示的打开方法:

    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivity(intent);
    

    由于打开关闭蓝牙都是需要时间的,所以蓝牙的状态和wifi类似一共有四种:

    BluetoothAdapter.STATE_ON  //已打开
    BluetoothAdapter.STATE_TURNING_ON //打开中
    BluetoothAdapter.STATE_OFF //已关闭
    BluetoothAdapter.STATE_TURNING_OFF //关闭中
    

    通过下面方法可以随时获取到蓝牙的状态:

    bluetoothAdapter.getState()
    

    这个方法一般用于界面的初始化,如要动态的更新,需要注册广播接受者实现,首先注册下面的广播:

    BluetoothAdapter.ACTION_STATE_CHANGED
    

    在广播接受者中获取状态并更新

    case BluetoothAdapter.ACTION_STATE_CHANGED:
          int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_OFF);
          updateState(state);
          break;
    

    2.发现周围设备

    在确保蓝牙开启后,我们可以搜索周围的蓝牙设备,开启搜索代码如下:

    bluetoothAdapter.startDiscovery();
    

    另外我们可以判断是否处于搜索状态,并决定是开启搜索或者是结束搜索

    if (bluetoothAdapter.isDiscovering())
        bluetoothAdapter.cancelDiscovery();
    else
        bluetoothAdapter.startDiscovery();
    

    为了不断获得已被搜索到的设备我们需要注册下面的广播:

    BluetoothDevice.ACTION_FOUND
    

    注册之后,如果有ACCESS_FINE_LOCATION权限,每搜索到一个设备,就会收到一个广播,这样我们就能更新我们的列表

    BluetoothDevice deviceFound = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    if (!listNearby.contains(deviceFound)) {
           listNearby.add(deviceFound);
           adapterNearby.notifyDataSetChanged();
    }
    

    由于搜索过程比较耗电,也会减慢蓝牙传输速度,所以我们并不能决定一次搜索的时长,不同的设备一次搜索的时间也不一样,所以我们需要知道搜索是否结束,可以注册下面的广播,实现动态更新:

    BluetoothAdapter.ACTION_DISCOVERY_FINISHED
    BluetoothAdapter.ACTION_DISCOVERY_STARTED
    

    两个广播分别在搜索开始和结束时发出。

    3.设备配对及取消配对

    配对实现比较简单,先拿到要配对设备的BluetoothDevice实例,然后一行代码

    device.createBond()
    

    由于配对是异步操作,我们若需要及时获得配对状态,需要注册下面广播:

    BluetoothDevice.ACTION_BOND_STATE_CHANGED
    

    当设备配对状态发送改变时会发出次广播,共有三种状态

    BluetoothDevice.BOND_NONE  //未配对
    BluetoothDevice.BOND_BONDING  //配对中
    BluetoothDevice.BOND_BONDED  //已配对
    

    此外我们还可以获得已配对的设备列表:

    bluetoothAdapter.getBondedDevices()
    

    已配对的设备不必经过搜索,可以直接连接。

    若想取消配对,暂时没有提供可用的方法调用,不过我们可以通过反射实现:

    try {
          Method method = BluetoothDevice.class.getMethod("removeBond");
          method.invoke(device);
    } catch (Exception e) {
          e.printStackTrace();
    }
    

    4.设备可见

    蓝牙设备有可见和不可见两种状态,不可见的设备能搜索周围的设备,但不能被周围设备发现,一般只启动搜索时,设备为不可见状态,若要被其他设备搜索到,我们要更改设备可见性:

    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,3000);
    startActivity(intent);
    

    其中BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION指的是可见时间,单位为秒,最大为300秒。

    5.设备连接与数据传输

    二者其实是一回事,要传输数据就要先建立连接,设备配对只是建立连接的前提,配对类似于记住该设备而已,连接是为了传输数据。

    蓝牙传输虽然用到了BluetoothServerSocket和BluetoothSocket,看着像是基于Socket,但是并没有什么关系,只是开发者为了便于我们开发,将模型做的和TCP类似而已。蓝牙是用MAC地址和UUID作为标志建立连接的,类似于IP地址和端口号。深层次的蓝牙传输协议我们这里并不深究,在Android上提供的接口已经极大地屏蔽的底层的细节,是我们开发变的非常方便。

    既然连接与传输的模型类似于TCP,那么BluetoothServerSocket就代表一个服务器实例,BluetoothSocket代表一个传输实例。简单来说就是服务端BluetoothServerSocket接受一个BluetoothSocket进行数据接受,客户端向服务端发送一个BluetoothSocket进行数据传递。

    一般可以在应用启动时就建立一个服务端,开启一个死循环,不断监听客户端的请求,不过要注意阻塞的处理。在发送数据时,利用子线程建立一个BluetoothSocket发送即可。

    在服务端,BluetoothServerSocket通过BluetoothAdapter的下面方法获取:

    public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
    

    name代表服务的可识别名称,可以任意指定,系统会将其写入数据库,UUID是一个标识符,这个比较重要,只有两端的UUID一样,连接才能建立,一般我们可以通过下面静态方法生成一个:

    public static UUID fromString(String name)
    

    在客户端BluetoothSocket可以通过BluetoothDevice的下面方法获取:

    public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid)
    

    可见这里只需传入对应的UUID 即可。

    在服务端,BluetoothServerSocket有一个阻塞方法accept()来监听连接请求,BluetoothSocket有一个阻塞方法connect()来建立连接,这里就和TCP模型类似了,接下来BluetoothSocket还有getInputStream()和getOutputStream()方法进行数据收发,基本和TCP实现一样,这里就不贴出代码了,详细可见我的一个简单demo

    相关文章

      网友评论

          本文标题:Android 蓝牙开发实践笔记

          本文链接:https://www.haomeiwen.com/subject/tmzklftx.html