Android 使用经典蓝牙

本文内容基本是按照安卓官方文档来进行经典蓝牙的学习,大体都从官方文档粘贴而来,英文部分做了写粗略的翻译,作为自己学习安卓经典蓝牙的一个记录。官方文档可参考https://developer.android.com/guide/topics/connectivity/bluetooth.html

基础知识

Android Bluetooth基础类、API,包含在android.bluetooth包下

  • BluetoothAdaper 表示本地蓝牙适配器,是所有蓝牙交互的入口点, BluetoothAdapter 是所有蓝牙交互的入口点。 利用它可以发现其他蓝牙设备,查询绑定(配对)设备的列表,使用已知的 MAC 地址实例化 BluetoothDevice,以及创建 BluetoothServerSocket 以侦听来自其他设备的通信。
  • BluetoothDevice 表示远程蓝牙设备。利用它可以通过 BluetoothSocket 请求与某个远程设备建立连接,或查询有关该设备的信息,例如设备的名称、地址、类和绑定状态等。
  • BluetoothSocket 表示蓝牙套接字接口(与 TCP Socket 相似)。这是允许应用通过 InputStream 和 OutputStream 与其他蓝牙设备交换数据的连接点。
  • BluetoothServerSocket 表示用于侦听传入请求的开放服务器套接字(类似于 TCP ServerSocket)。 要连接两台 Android 设备,其中一台设备必须使用此类开放一个服务器套接字。 当一台远程蓝牙设备向此设备发出连接请求时, BluetoothServerSocket 将会在接受连接后返回已连接的 BluetoothSocket
  • BluetoothClass 描述蓝牙设备的一般特征和功能。 这是一组只读属性,用于定义设备的主要和次要设备类及其服务。 不过,它不能可靠地描述设备支持的所有蓝牙配置文件和服务,而是适合作为设备类型提示
  • BluetoothProfile 表示蓝牙配置文件的接口。 蓝牙配置文件是适用于设备间蓝牙通信的无线接口规范。 免提配置文件便是一个示例。 如需了解有关配置文件的详细讨论,请参阅使用配置文件

使用蓝牙

获取蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
设置蓝牙
  • 获取BluetoothAdapter

    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null) {// 若为Null,则说明不支持蓝牙,在这里做出相应处理
    }
    
  • 启用蓝牙

    if (!mBluetoothAdapter.isEnabled()) {Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }
    

    若蓝牙为启用,则将显示对话框,请求用户允许启用蓝牙

查找蓝牙
  • 查询配对的设备:在执行设备发现之前,有必要查询已配对的设备及,故使用getBondedDevices()方法,其返回一组已配对的BluetoothDevice

    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
    // 如果有配对的设备
    if (pairedDevices.size() > 0) {// 遍历匹配设备组for (BluetoothDevice device : pairedDevices) {// 将已配对设备的名称及地址存储在一个ArrayAdapter中mArrayAdapter.add(device.getName() + "\n" + device.getAddress());}
    }
    
  • 发现设备:要开始发现设备,只需调用 startDiscovery()。该进程为异步进程,并且该方法会立即返回一个布尔值,指示是否已成功启动发现操作。 发现进程通常包含约 12 秒钟的查询扫描,之后对每台发现的设备进行页面扫描,以检索其蓝牙名称。您的应用必须针对 ACTION_FOUND Intent 注册一个 BroadcastReceiver,以便接收每台发现的设备的相关信息。 针对每台设备,系统将会广播 ACTION_FOUNDIntent。此 Intent 将携带额外字段 EXTRA_DEVICEEXTRA_CLASS,二者分别包含 BluetoothDeviceBluetoothClass。 例如,下面说明了在发现设备时如何注册以处理广播。

    // 为 ACTION_FOUND 创建一个广播接收器
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {public void onReceive(Context context, Intent intent) {String action = intent.getAction();//当发现一个蓝牙设备时if (BluetoothDevice.ACTION_FOUND.equals(action)) {//利用EXTRA_DEVICE字段, 从intent中获取该设备实例BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);// 使用getName和getAddress方法获取蓝牙设备名和地址mArrayAdapter.add(device.getName() + "\n" + device.getAddress());}}
    };
    //注册该广播
    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    //搜索完成
    filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    registerReceiver(mReceiver, filter); // 要在onDestry方法中取消注册该广播
    
    //取消注册该广播
    @Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(mBtFoundReceiver);}
    
  • 启用本地可检测性(可选)

    • 如果您希望将本地设备设为可被其他设备检测到,请使用ACTION_REQUEST_DISCOVERABLE 操作 Intent 调用 startActivityForResult(Intent, int)。 这将通过系统设置发出启用可检测到模式的请求(无需停止您的应用)。 默认情况下,设备将变为可检测到并持续 120 秒钟。 您可以通过添加 EXTRA_DISCOVERABLE_DURATION Intent Extra 来定义不同的持续时间。 应用可以设置的最大持续时间为 3600 秒,值为 0 则表示设备始终可检测到。 任何小于 0 或大于 3600 的值都会自动设为 120 秒。

      //设置持续时间为300秒
      Intent discoverableIntent = new
      Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
      discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
      startActivity(discoverableIntent);
      

      上述操作将显示对话框,请求用户允许设备设为可检测到,若同意,则您的 Activity 将会收到对 onActivityResult()) 回调的调用,其结果代码等于设备可检测到的持续时间。 如果用户响应“No”或出现错误,结果代码将为 RESULT_CANCELED如果设备尚未启用蓝牙,则在启用设备可检测性将会自动启用蓝牙。仅当您希望您的应用托管将用于接受传入连接的服务器套接字时,才有必要启用可检测性,因为远程设备必须能够发现该设备,然后才能发起连接。

连接设备

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

连接为服务器

当您需要连接两台设备时,其中一台设备必须通过保持开放的 BluetoothServerSocket 来充当服务器。 服务器套接字的用途是侦听传入的连接请求,并在接受一个请求后提供已连接的 BluetoothSocket。 从 BluetoothServerSocket 获取 BluetoothSocket 后,可以(并且应该)舍弃 BluetoothServerSocket,除非您需要接受更多连接。

  • 基本流程

    • 通过调用 listenUsingRfcommWithServiceRecord(String, UUID) 获取 BluetoothServerSocket

      • UUID: 通用唯一标识符,是用于唯一标识信息的字符串ID的128为标准化格式
      • UUID将作为与客户端设备连接协议的基础,即当客户端尝试连接此设备时,他会携带一个想要连接的服务的UUID,两个UUID必须匹配
    • 调用accept()开始侦听连接请求,操作成功,accept将返回已连接的BluetoothSocket

    • 调用 close()。这将释放服务器套接字及其所有资源,但不会关闭 accept() 所返回的已连接的 BluetoothSocket

    • 注意:accept()不应再主Activity UI线程中执行,因为它是阻塞调用。应开启一个新线程,使用 BluetoothServerSocketBluetoothSocket 完成所有工作。要终止 accept() 等被阻塞的调用,请通过另一个线程在 BluetoothServerSocket(或 BluetoothSocket)上调用 close(),被阻塞的调用将会立即返回

      //一个用于接受传入连接的服务器组件的简化线程
      private class AcceptThread extends Thread {private final BluetoothServerSocket mmServerSocket;public AcceptThread() {// 使用一个临时变量,之后再将它赋值给mmServerSocket,因为mmServerSocket是一个final变量BluetoothServerSocket tmp = null;try {// MY_UUID是此应用的UUID串,同时也被客户端使用tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);} catch (IOException e) { }//赋值mmServerSocket = tmp;}public void run() {BluetoothSocket socket = null;// 持续侦听直到返回了一个socket或者发生异常while (true) {try {socket = mmServerSocket.accept();} catch (IOException e) {break;}// 连接建立成功if (socket != null) {// 在一个单独的线程中管理连接,manageConnectedSocket为虚构的方法,它将启动用于传输数据的线程manageConnectedSocket(socket);mmServerSocket.close();break;}}}/** 将取消侦听socket,并使进程关闭 */public void cancel() {try {mmServerSocket.close();} catch (IOException e) { }}
      }
      
连接为客户端

要发起与远程设备(保持开放的服务器套接字的设备)的连接,必须首先获取表示该远程设备的 BluetoothDevice 对象。(在前面有关查找设备的部分介绍了如何获取 BluetoothDevice)。 然后必须使用 BluetoothDevice 来获取 BluetoothSocket 并发起连接。

基本过程

  • 使用 BluetoothDevice,通过调用 createRfcommSocketToServiceRecord(UUID) 获取 BluetoothSocket。此处使用的UUID必须与服务器设备在使用 listenUsingRfcommWithServiceRecord(String, UUID) 开放其BluetoothServerSocket 时所用的 UUID 相匹配。 要使用相同的 UUID,只需将该 UUID 字符串以硬编码方式编入应用,然后通过服务器代码和客户端代码引用该字符串。

  • 通过connect()发起连接。执行此调用时,系统将会在远程设备上执行 SDP 查找,以便匹配 UUID。 如果查找成功并且远程设备接受了该连接,它将共享 RFCOMM 通道以便在连接期间使用,并且 connect() 将会返回。 此方法为阻塞调用。 如果由于任何原因连接失败或 connect() 方法超时(大约 12 秒之后),它将会引发异常。

    由于 connect() 为阻塞调用,因此该连接过程应始终在主 Activity 线程以外的线程中执行

    注意:注:在调用 connect() 时,应始终确保设备未在执行设备发现。 如果正在进行发现操作,则会大幅降低连接尝试的速度,并增加连接失败的可能性。

    //发起蓝牙连接的线程的基本示例
    private class ConnectThread extends Thread {private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device) {// Use a temporary object that is later assigned to mmSocket,// because mmSocket is finalBluetoothSocket tmp = null;mmDevice = device;// Get a BluetoothSocket to connect with the given BluetoothDevicetry {// MY_UUID is the app's UUID string, also used by the server codetmp = device.createRfcommSocketToServiceRecord(MY_UUID);} catch (IOException e) { }mmSocket = tmp;}public void run() {// Cancel discovery because it will slow down the connectionmBluetoothAdapter.cancelDiscovery();try {// Connect the device through the socket. This will block// until it succeeds or throws an exceptionmmSocket.connect();} catch (IOException connectException) {// Unable to connect; close the socket and get outtry {mmSocket.close();} catch (IOException closeException) { }return;}// Do work to manage the connection (in a separate thread)manageConnectedSocket(mmSocket);}/** Will cancel an in-progress connection, and close the socket */public void cancel() {try {mmSocket.close();} catch (IOException e) { }}
    }
    
管理连接
  • 获取 InputStreamOutputStream,二者分别通过套接字以及 getInputStream()getOutputStream() 来处理数据传输。
  • 使用 read(byte[])write(byte[]) 读取数据并写入到流式传输。
//传输内容示例
private class ConnectedThread extends Thread {private final BluetoothSocket mmSocket;private final InputStream mmInStream;private final OutputStream mmOutStream;public ConnectedThread(BluetoothSocket socket) {//获取socket及输入输出流mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;try {tmpIn = socket.getInputStream();tmpOut = socket.getOutputStream();} catch (IOException e) { }mmInStream = tmpIn;mmOutStream = tmpOut;}public void run() {byte[] buffer = new byte[1024];  int bytes; 获取read()返回的数据// 持续侦听输入流,知道发生异常while (true) {try {从输入流中读取数据bytes = mmInStream.read(buffer);// 将包含的字节流发送到UI activitymHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();} catch (IOException e) {break;}}}/* 在主Activity调用此方法,用以向远程设备发送数据 */public void write(byte[] bytes) {try {mmOutStream.write(bytes);} catch (IOException e) { }}/* 在主activity调用此方法用以关闭连接*/public void cancel() {try {mmSocket.close();} catch (IOException e) { }}
}

Android 使用经典蓝牙相关推荐

  1. 基于Android Studio经典蓝牙APP---继上一次的完善版

    基于Android Studio经典蓝牙APP-继上一次的完善版 考虑到好友网友们反馈的问题总结了以下几点: 1.工程下载爆红:版本问题-gradle:4.1.1. 2.无接收数据功能,怎么实现:这里 ...

  2. android ble 经典蓝牙,Android 经典蓝牙(Classic Bluetooth)和低功耗蓝牙(BLE)

    [实例简介] 从蓝牙4.0开始包含两个蓝牙芯片模块:传统/经典蓝牙模块(Classic Bluetooth,简称BT)和低功耗蓝牙(Bluetooth Low Energy,简称BLE) 经典蓝牙是在 ...

  3. Android笔记---蓝牙开发经典蓝牙和低功耗蓝牙

    目录 前言 一般开发步骤 相关API介绍 一.通用API 1.BluetoothAdapter 2.BluetoothDevice 二.经典蓝牙(BT)API 1.BluetoothSocket 2. ...

  4. Android经典蓝牙开发简介(Google官网译文)

    公司的项目最近需要用到蓝牙开发的相关内容,因此特地查阅了Google官方文档的内容并进行二次整理,希望能对需要学习该部分的朋友有所帮助. 原文地址:http://developer.android.c ...

  5. 【Android】蓝牙开发——经典蓝牙:配对与解除配对 实现配对或连接时不弹出配对框

    目录 一.配对方法 二.解除配对方法 三.配对/解除配对结果 四.justwork配对模式下,不弹出配对框 五.pincode配对模式下,不弹出配对框 六.小结 在之前的文章[Android]蓝牙开发 ...

  6. Android蓝牙开发前序知识-经典蓝牙低功耗蓝牙区别

    最近从网上搜了(抄了)一些经典蓝牙和低功耗蓝牙的区别.关于蓝牙的大概了解,前文已经描述过了. Android中的蓝牙 说到Android中的蓝牙,大家听到的可能有蓝牙1.0.蓝牙2.0.蓝牙3.0.蓝 ...

  7. Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别

    找到一篇介紹BT与BLE使用差别的文章, 写的很清晰,看完基本明白了 ----------------------------------------------------------------- ...

  8. Android蓝牙开发 — 经典蓝牙BLE蓝牙

    一,前期基础知识储备 1)蓝牙是一种支持设备之间短距离通信的无线电技术(其他还包括红外,WIFI): 支持移动电话.笔记本电脑.无线耳机等设备之间进行信息的交换: Android支持的蓝牙协议栈:Bl ...

  9. Android蓝牙开发—经典蓝牙详细开发流程

    文章目录 开发流程 权限 核心API BlueToothAdapter getDefaultAdapter():获取BluetoothAdapter对象 判断设备是否支持蓝牙 判断蓝牙是否开启 get ...

最新文章

  1. couchdb 自动生成html,如何在CouchDB中使用html模板
  2. mysql缓存 碎片_Mysql查询缓存碎片、缓存命中率及Nagios监控
  3. Win10最常用的快捷键,效率Max提高100%(常用的应该是最全的)
  4. 小米10至尊纪念版DXO第一 雷军:这是小米打拼三年第二次登顶
  5. 8g ubuntu 树莓派4b_树莓派4B如何安装ubuntu20.04
  6. 文字处理技术:最小布局
  7. 抗光幕布哪个牌子好?
  8. 自己写的vue图片上传插件(假装是插件)
  9. 22071班华清远见(上海中心)
  10. ffmpeg C++推流
  11. pandas数据处理基础之标准化与标签数值化
  12. 【Python】使用pandas_datareader获取股票信息并进行可视化分析
  13. 黑帽seo 模板生成php,全自动无限生成关键词页面(黑帽SEO优化终极方法)
  14. Crazy Learning for Day 7
  15. 气象绘图中出现白条该如何解决
  16. ubnt 配置软件 android,Ubnt EdgeRouter 路由器中国移动原生IPv6配置
  17. 2021祥云杯部分pwn
  18. 发帖需要验证手机-解决办法
  19. 滴滴如何调度_滴滴调度小助手和热力图是司机的陷阱还是馅饼?
  20. 怎样记住Integer的最大值(有趣的思维和搞笑的回答)

热门文章

  1. aa44444444444
  2. 大学网课查题公众号 查题公众号搭建 对接题库
  3. Jupyter notebook常用快捷键
  4. vue-cli-service build 环境设置
  5. 【转载】在Python中安装GDAL(最简单,最详细图文教程)
  6. 学习 Java 的历程和体会『写给新手看,欢迎老司机批评和建议』
  7. 情人节礼物------用她的照片和我们的聊天记录生成词云~
  8. vue ssr之nuxt
  9. 软件工程——软件管理
  10. Python 儒略日和公历互转代码