蓝牙初步认识--Google官网文档

蓝牙

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

使用 Bluetooth API,Android 应用可执行以下操作:

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

本文将介绍如何使用传统蓝牙。传统蓝牙适用于电池使用强度较大的操作,例如 Android 设备之间的流式传输和通信等。 针对具有低功耗要求的蓝牙设备,Android 4.3(API 级别 18)中引入了面向低功耗蓝牙的 API 支持。 如需了解更多信息,请参阅低功耗蓝牙

基础知识

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

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

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

BluetoothAdapter

表示本地蓝牙适配器(蓝牙无线电)。这 BluetoothAdapter是所有蓝牙互动的入门点。使用此功能,您可以发现其他蓝牙设备,查询已绑定(配对)设备的列表,BluetoothDevice使用已知的MAC地址实例化,并创建一个BluetoothServerSocket监听来自其他设备的通信。

BluetoothDevice

表示远程蓝牙设备。使用此方法通过BluetoothSocket关于设备的或查询信息(如其名称,地址,类别和绑定状态)来请求与远程设备的连接。

BluetoothSocket

表示蓝牙插座的接口(类似于TCP Socket)。这是允许应用程序通过InputStream和OutputStream与另一个蓝牙设备交换数据的连接点。

BluetoothServerSocket

表示用于侦听传入请求(类似于TCP ServerSocket)的打开的服务器套接字。为了连接两个Android设备,一个设备必须打开这个类的服务器套接字。当远程蓝牙设备向该设备发出连接请求时,当接受BluetoothServerSocket连接BluetoothSocket时, 将返回连接。

BluetoothClass

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

BluetoothProfile

表示蓝牙配置文件的界面。甲蓝牙配置文件是用于在设备之间基于蓝牙的通信的无线接口规范。一个例子是免提配置文件。有关配置文件的更多讨论,请参阅使用配置文件

BluetoothHeadset

支持蓝牙耳机与手机配合使用。这包括蓝牙耳机和免提(v1.5)配置文件。

BluetoothA2dp

定义通过蓝牙连接将高质量的音频流从一个设备传输到另一个设备。“A2DP”表示高级音频分配配置文件。

BluetoothHealth

表示控制蓝牙服务的运行状况设备配置文件代理。

BluetoothHealthCallback

用于实现BluetoothHealth回调的抽象类。您必须扩展此类并实现回调方法以接收有关应用程序注册状态和蓝牙通道状态更改的更新。

BluetoothHealthAppConfiguration

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

BluetoothProfile.ServiceListener

BluetoothProfile当IPC客户端连接到服务器或与服务断开连接(即运行特定配置文件的内部服务器)时,可以通知IPC客户端。

蓝牙权限

要在应用中使用蓝牙功能,必须声明蓝牙权限BLUETOOTH,您需要此权限才能执行任何蓝牙通信,例如请求连接、接受连接和传输数据等。

如果您希望您的应用启动设备发现或操作蓝牙设置,则还必须声明 BLUETOOTH_ADMIN 权限。 大多数应用需要此权限仅仅为了能够发现本地蓝牙设备。 除非该应用是将要应用户请求修改蓝牙设置的“超级管理员”,否则不应使用此权限所授予的其他能力。

Android6.0及以上需要位置权限才能扫描到设备;位置权限是危险权限,需要

    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
    <uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

设置蓝牙

  1. 获取 BluetoothAdapter
    将BluetoothAdapter所需的任何和所有的蓝牙活动。要获取BluetoothAdapter,请调用静态getDefaultAdapter()方法。这将返回一个 BluetoothAdapter表示设备自己的蓝牙适配器(蓝牙无线电)的。整个系统有一个蓝牙适配器,您的应用程序可以使用此对象与其进行交互。如果 getDefaultAdapter()返回null,则设备不支持蓝牙,您的操作将在此结束。例如:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // Device does not support Bluetooth
}

2.启用蓝牙
接下来,您需要确保启用蓝牙。调用isEnabled()检查蓝牙是否当前启用。如果此方法返回false,则蓝牙被禁用。要请求启用蓝牙,请startActivityForResult() 使用ACTION_REQUEST_ENABLEIntent操作调用。这将发出通过系统设置启用蓝牙的请求(不停止您的应用程序)。例如:

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

查找设备

使用该功能BluetoothAdapter,您可以通过设备发现或通过查询配对(绑定)设备列表来查找远程蓝牙设备。

设备发现是一种扫描过程,可以在本地搜索蓝牙设备,然后请求一些关于每个设备的信息(这有时被称为“发现”,“查询”或“扫描”)。但是,本地区内的蓝牙设备只有在当前启用才能发现的情况下才能响应发现请求。如果设备是可发现的,它将通过共享一些信息来响应发现请求,例如设备名称,类别及其唯一的MAC地址。使用此信息,执行发现的设备随后可以选择启动与发现的设备的连接。

一旦与第一次使用远程设备进行连接,配对请求将自动呈现给用户。当设备配对时,将保存有关该设备的基本信息(如设备名称,类和MAC地址),并使用蓝牙API进行读取。使用已知的MAC地址进行远程设备,可以在任何时间启动连接,而无需执行发现(假定设备在范围内)。

记住配对和连接之间有区别。要配对意味着两个设备都知道彼此的存在,具有可以用于认证的共享链路密钥,并且能够建立彼此的加密连接。要连接意味着设备当前共享RFCOMM信道,并且能够彼此传输数据。在建立RFCOMM连接之前,目前的Android蓝牙API需要配对设备。(当您使用蓝牙API启动加密连接时,会自动执行配对。)

以下部分介绍如何查找已配对的设备,或使用设备发现来发现新设备。

查找配对的设备

在执行设备发现之前,它值得查询一组配对的设备,以查看所需设备是否已知。要这样做,打电话getBondedDevices()。这将返回一组BluetoothDevice代表配对的设备。例如,您可以查询所有配对的设备,然后使用ArrayAdapter向用户显示每个设备的名称:

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

发现设备

要开始发现设备,只需调用startDiscovery()。该进程是异步的,该方法将立即返回一个布尔值,指示发现是否已成功启动。发现过程通常涉及大约12秒的查询扫描,随后是每个找到的设备的页面扫描以检索其蓝牙名称。

你的应用程序必须登记为action_found意图接收有关每个设备发现BroadcastReceiver。对每一个设备,系统将播出action_found意图。这种意图进行额外的领域extra_device和extra_class,包含一个蓝牙设备和蓝牙类,分别。例如,在这里的你如何登记办理广播设备时发现的:

// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

连接设备

为了在两个设备之间创建应用程序之间的连接,您必须同时实现服务器端和客户端机制,因为一个设备必须打开一个服务器套接字,另一个设备必须启动连接(使用服务器设备的MAC地址启动连接)。当服务器和客户端都BluetoothSocket在相同的RFCOMM通道上连接时,服务器和客户端被认为是相互连接 的。此时,每个设备可以获取输入和输出流,并且可以开始数据传输,这将在“ 管理连接 ”一节中讨论。本节介绍如何启动两台设备之间的连接。

服务器设备和客户端设备都BluetoothSocket以不同的方式获得所需的设备。当接收到连接时,服务器将收到该消息。当客户端向服务器打开RFCOMM通道时,客户端将收到该消息。

一种实现技术是将每个设备自动准备为服务器,以便每个设备都有一个服务器套接字打开并监听连接。那么任一设备都可以启动与其他设备的连接并成为客户端。或者,一个设备可以显式地“主持”连接并按需打开服务器套接字,而另一个设备可以简单地启动连接。

连接为服务器

当您要连接两个设备时,必须通过持续打开来充当服务器BluetoothServerSocket。服务器套接字的目的是监听传入的连接请求,并且当被接受时,提供连接BluetoothSocket。当从BluetoothServerSocket获取BluetoothSocket时,BluetoothServerSocket可以(应该)被丢弃,除非你想接受更多的连接。

以下是设置服务器套接字并接受连接的基本步骤:

  1. 获得BluetoothServerSocket通过调用 listenUsingRfcommWithServiceRecord(String, UUID)。
    该字符串是您的服务的可标识名称,系统将自动写入设备上的新服务发现协议(SDP)数据库条目(名称是任意的,可以仅仅是您的应用程序名称)。UUID也包含在SDP条目中,并将作为与客户端设备的连接协议的基础。也就是说,当客户端尝试与此设备连接时,它将携带唯一标识要连接的服务的UUID。这些UUID必须匹配才能接受连接(在下一步中)。
  2. 通过调用开始监听连接请求 accept()。
    这是一个阻塞调用。当连接被接受或发生异常时,它将返回。仅当远程设备发送了一个与该侦听服务器套接字注册的UUID相匹配的UUID的连接请求时才接受连接。当成功时,accept()将返回一个已连接BluetoothSocket。
  3. 除非你想接受额外的连接,请打电话 close()。
    这将释放服务器socket和它的所有资源,但并没有关闭连接的BluetoothSocket一个已经被退回accept()。不像TCP / IP,RFCOMM只允许每个信道的一个连接的客户端的时间,所以在大多数情况下是有意义的调用close()在BluetoothServerSocket接受连接的套接字之后。

accept()调用不应在主 Activity UI 线程中执行,因为它是阻塞调用,并会阻止与应用的任何其他交互。 在您的应用所管理的新线程中使用 BluetoothServerSocket 或 BluetoothSocket完成所有工作,这通常是一种行之有效的做法。 要终止 accept() 等被阻塞的调用,请通过另一个线程在 BluetoothServerSocket或 BluetoothSocket上调用 close(),被阻塞的调用将会立即返回。 请注意,BluetoothServerSocket或 BluetoothSocket 中的所有方法都是线程安全的方法。

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
        mmServerSocket = tmp;
    }

    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // If a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }

    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}

连接为客户端

为了启动与远程设备(持有打开服务器套接字的设备)的连接,您必须首先获取BluetoothDevice表示远程设备的对象。(BluetoothDevice有关查找设备的上述部分将介绍以下部分)。然后,您必须使用它 BluetoothDevice来获取BluetoothSocket并启动连接。

这是基本的过程:

使用BluetoothDevice,BluetoothSocket通过调用得到一个createRfcommSocketToServiceRecord(UUID)。
这将初始化一个BluetoothSocket将连接到的BluetoothDevice。在此处传递的UUID必须与服务器设备打开BluetoothServerSocket(使用listenUsingRfcommWithServiceRecord(String, UUID))时使用的UUID相匹配 。使用相同的UUID只是将UUID字符串硬编码到应用程序中,然后从服务器和客户端代码引用它。
通过调用启动连接connect()。
在此呼叫之后,系统将在远程设备上执行SDP查找,以匹配UUID。如果查找成功并且远程设备接受连接,则它将共享在连接期间使用的RFCOMM通道并connect()返回。这种方法是一个阻塞调用。如果出于任何原因,连接失败或connect()方法超时(约12秒钟后),则会引发异常。
因为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 final
        BluetoothSocket tmp = null;
        mmDevice = device;

        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try {
            // MY_UUID is the app's UUID string, also used by the server code
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) { }
        mmSocket = tmp;
    }

    public void run() {
        // Cancel discovery because it will slow down the connection
        mBluetoothAdapter.cancelDiscovery();

        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            try {
                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) { }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280

推荐阅读更多精彩内容

  • 蓝牙 注:本文翻译自https://developer.android.com/guide/topics/conn...
    RxCode阅读 8,485评论 11 99
  • Android 平台包含蓝牙网络堆栈支持,凭借此项支持,设备能以无线方式与其他蓝牙设备交换数据。应用框架提供了通过...
    虎三呀阅读 745评论 0 1
  • 公司的项目最近需要用到蓝牙开发的相关内容,因此特地查阅了Google官方文档的内容并进行二次整理,希望能对需要学习...
    Chuckiefan阅读 32,256评论 44 123
  • 最近项目使用蓝牙,之前并没有接触,还是发现了很多坑,查阅了很多资料,说的迷迷糊糊,今天特查看官方文档。 说下遇到的...
    King9527阅读 1,702评论 0 1
  • 好想喊一声:妈妈 ———写在母亲节的话 妈妈,妈妈 每次我回家 就会喊一声妈妈 我回来了 这时候 就会有一声应答 ...
    牛你阅读 403评论 0 0