蓝牙

Android平台包括对蓝牙网络堆栈的支持,允许设备与其他蓝牙设备进行无线交换数据。应用程序框架通过Android蓝牙API提供对蓝牙功能的访问。这些API让应用程序无线连接到其他蓝牙设备,实现点对点和多点无线功能。

使用蓝牙API,Android应用程序可以执行以下操作:

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

本文档介绍如何使用Classic蓝牙。经典蓝牙是更多电池密集型操作(如Android设备之间的流媒体和通信)的理想选择。对于低功耗要求的蓝牙设备,Android 4.3(API Level 18)为蓝牙低功耗引入了API支持。要了解更多信息,请参阅蓝牙低功耗

基础


本文档介绍如何使用Android蓝牙API完成使用蓝牙进行通信所需的四个主要任务:设置蓝牙,查找本地配对或可用的设备,连接设备以及在设备之间传输数据。

所有的蓝牙API都可以在android.bluetooth包中使用。以下是创建蓝牙连接所需的类和接口的总结:

 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 权限。大多数应用程序仅需要此权限才能发现本地蓝牙设备。除非应用程序是根据用户请求修改蓝牙设置的“电源管理器”,否则不得使用此权限授予的其他功能。注意:如果您使用BLUETOOTH_ADMIN权限,那么您还必须具有该BLUETOOTH权限。

声明您的应用程序清单文件中的蓝牙权限。例如:

<uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
设置蓝牙

在您的应用程序可以通过蓝牙进行通信之前,您需要验证设备是否支持蓝牙,如果是,请确保已启用蓝牙。

如果不支持蓝牙,则应优雅地禁用任何蓝牙功能。如果支持蓝牙但禁用蓝牙,则可以要求用户在不离开应用程序的情况下启用蓝牙。这个设置是通过两个步骤完成的BluetoothAdapter。

  1. 得到 BluetoothAdapter
    将BluetoothAdapter所需的任何和所有的蓝牙活动。要获取BluetoothAdapter,请调用静态getDefaultAdapter()方法。这将返回一个 BluetoothAdapter表示设备自己的蓝牙适配器(蓝牙无线电)的。整个系统有一个蓝牙适配器,您的应用程序可以使用此对象与其进行交互。如果 getDefaultAdapter()返回null,则设备不支持蓝牙,您的故事将在此结束。例如:
//获取蓝牙适配器
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter != null) {
            Toast.makeText(this, "支持蓝牙设备!", Toast.LENGTH_SHORT).show();
        }
  1. 启用蓝牙
    接下来,您需要确保启用蓝牙。呼叫isEnabled()检查蓝牙是否当前启用。如果此方法返回false,则蓝牙被禁用。要请求启用蓝牙,请startActivityForResult() 使用ACTION_REQUEST_ENABLEIntent操作调用。这将发出通过系统设置启用蓝牙的请求(不停止您的应用程序)。例如:
if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

将显示一个对话框,要求用户启用蓝牙功能,如图1所示。如果用户响应“是”,系统将开始启用蓝牙,一旦进程完成(或失败),系统将重新启动应用程序。
REQUEST_ENABLE_BT传递给的常数startActivityForResult()是一个本地定义的整数(必须大于0),系统将在onActivityResult()实现中作为 requestCode参数传回给您 。
如果启用蓝牙成功,您的活动将RESULT_OK在onActivityResult() 回调中接收结果代码。如果由于错误(或用户响应“否”)未启用蓝牙,则结果代码为RESULT_CANCELED。
可选地,您的应用程序还可以监听 ACTION_STATE_CHANGED广播Intent,当Bluetooth状态发生变化时,系统将广播Int Int。该广播包含额外的字段EXTRA_STATE和EXTRA_PREVIOUS_STATE,包含新老的蓝牙状态,分别。这些额外的字段可能的值是 STATE_TURNING_ON,STATE_ON,STATE_TURNING_OFF,和STATE_OFF。聆听此广播可能会有助于在您的应用程序运行时检测对蓝牙状态所做的更改。

提示:启用可发现性将自动启用蓝牙。如果您打算在执行蓝牙活动之前始终启用设备可发现性,则可以跳过上述步骤2。

查找设备


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

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

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

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

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

注意:默认情况下,Android驱动的设备不可发现。用户可以通过系统设置在有限的时间内发现设备,或者应用程序可以请求用户在不离开应用程序的情况下启用可发现性。

查询配对设备

在执行设备发现之前,它值得查询一组配对的设备,以查看所需设备是否已知。要这样做,打电话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());
    }
}

从BluetoothDevice对象开始连接所需要的就是MAC地址。在这个例子中,它被保存为向用户显示的ArrayAdapter的一部分。以后可以提取MAC地址以便启动连接。您可以在有关连接设备的部分中了解有关创建连接的更多信息。

发现设备

要开始发现设备,只需调用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地址。在这个例子中,它是保存为一个ArrayAdapter,显示给用户的一部分。MAC地址可以被提取为启动连接。你可以了解更多关于创建在该段的连接对连接设备。

注意:执行设备发现是蓝牙适配器的重要过程,将消耗大量资源。找到设备进行连接后,确保您cancelDiscovery()在尝试连接之前始终停止发现 。此外,如果您已经持有与设备的连接,则执行发现可以显着减少连接可用的带宽,因此在连接时不应执行发现。

实现可发现性
如果要将本地设备发现到其他设备,请startActivityForResult(Intent, int)通过 ACTION_REQUEST_DISCOVERABLEIntent操作调用。这将通过系统设置(不停止应用程序)发出启用可发现模式的请求。默认情况下,设备可以发现120秒。您可以通过添加EXTRA_DISCOVERABLE_DURATIONIntent extra 来定义不同的持续时间 。应用可以设置的最长持续时间为3600秒,值为0表示设备始终可以发现。低于0或高于3600的值自动设置为120秒)。例如,此代码段将持续时间设置为300:

Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);

将显示一个对话框,请求用户许可使设备可以发现,如图2所示。如果用户响应“是”,则设备将在指定的时间内被发现。然后,您的活动将收到对onActivityResult())回调的呼叫,结果代码等于设备可发现的持续时间。如果用户响应“否”,或者发生错误,结果代码将会出现RESULT_CANCELED。

注意:如果设备上尚未启用蓝牙,则启用设备可发现性将自动启用蓝牙。

设备将默认保持在可发现的模式下分配的时间。如果您希望在可发现模式发生变化时收到通知,您可以为ACTION_SCAN_MODE_CHANGED Intent 注册BroadcastReceiver 。这将包含额外的字段,EXTRA_SCAN_MODE并 EXTRA_PREVIOUS_SCAN_MODE分别告诉你新的和旧的扫描模式。对于每个可能的值是 SCAN_MODE_CONNECTABLE_DISCOVERABLE, SCAN_MODE_CONNECTABLE,或SCAN_MODE_NONE,其中指示该设备是可发现模式,而不是在可发现模式,但仍然能够接收在可发现模式的连接,或不和无法分别接收连接,。

如果要启动与远程设备的连接,则不需要启用设备的可发现性。只有当您希望应用程序托管将接受传入连接的服务器套接字时,才能实现可发现性,因为远程设备必须能够发现设备才能启动连接。

连接设备

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


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

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

注意:如果两个设备之前没有配对,则Android框架将在连接过程中自动向用户显示配对请求通知或对话框,如图3所示。因此,当尝试连接设备时,您的应用程序不会需要关心设备是否配对。您的RFCOMM连接尝试将阻止,直到用户成功配对,否则将失败,如果用户拒绝配对,或配对失败或超时。

作为服务器连接

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

关于UUID
通用唯一标识符(UUID)是用于唯一标识信息的字符串ID的标准化128位格式。UUID的观点是,它足够大,您可以选择任意随机,它不会发生冲突。在这种情况下,它用于唯一标识您的应用程序的蓝牙服务。要获得UUID与你的应用程序中使用,你可以在Web上使用的许多随机UUID发电机中的一个,然后初始化UUID用fromString(String)。

以下是设置服务器套接字并接受连接的基本步骤:
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()电话不应该在主要活动UI线程,因为它是一个阻塞调用,并防止对应用程序的任何其他交互来执行。使用一个BluetoothServerSocket或BluetoothSocket一个由您的应用程序管理的新线程来完成所有工作通常是有意义的。要中止阻塞呼叫诸如accept(),呼叫close()在BluetoothServerSocket(或BluetoothSocket从另一个线程)和封端的呼叫将立即返回。注意所有方法在一个BluetoothServerSocket或者BluetoothSocket 是线程安全的。

example

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) { }
    }
}

在此示例中,只需要一个传入连接,因此一旦接收到连接并且BluetoothSocket被获取,应用程序将采集的发送BluetoothSocket到单独的线程,关闭 BluetoothServerSocket并中断循环。

请注意,当accept() 返回BluetoothSocket时,套接字已连接,所以你应该不叫connect()(当你从客户端做的)。

manageConnectedSocket()是应用程序中的虚构方法,它将启动用于传输数据的线程,这将在“ 管理连接 ”一节中讨论。

BluetoothServerSocket 一旦听完传入的连接,你应该通常关闭你。在这个例子中,close()一旦BluetoothSocket被获取就被调用。您可能还希望在您的线程中提供一个公共方法,可以BluetoothSocket在需要停止在服务器套接字上侦听的情况下关闭私有的方法。

作为客户端连接

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

这是基本的过程:

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

注意:调用时,应始终确保设备未执行设备发现connect()。如果发现正在进行中,则连接尝试将显着减慢,并且更有可能失败。

example

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) { }
    }
}

注意 cancelDiscovery()在连接之前调用。您应该在连接之前始终执行此操作,并且无需实际检查是否正在运行就可以安全地进行呼叫(但如果您想要检查,调用isDiscovering())。

manageConnectedSocket()是应用程序中的虚构方法,它将启动用于传输数据的线程,这将在“ 管理连接 ”一节中讨论。

当你完成你的工作BluetoothSocket,总是打电话close()来清理。这样做将立即关闭连接的套接字并清理所有内部资源。

管理连接

当您已成功连接两台(或更多)设备时,每台设备都将连接BluetoothSocket。这是乐趣开始的地方,因为您可以在设备之间共享数据。使用BluetoothSocket,一般程序传输任意数据很简单:

  1. 获取InputStream并OutputStream通过套接字句柄传输,通过getInputStream()和 getOutputStream()分别。
  2. 读取和写入数据流read(byte[])和write(byte[])。
    而已。

当然还有实施细节要考虑。首先,您应该使用专用的线程进行所有流的读写。这是因为这两种重要read(byte[])和write(byte[])方法阻塞调用。read(byte[])将阻止,直到从流中读取东西。write(byte[])通常不会阻塞,但是如果远程设备没有read(byte[])足够快地调用并且中间缓冲区已满,则可以阻止流控制。所以,你的线程中的主循环应该是专门从中读取的InputStream。线程中的一个单独的公共方法可以用来启动写入OutputStream。

example
下面是一个如何看待的这个例子:

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the input and output streams, using temp objects because
        // member streams are final
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) { }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        byte[] buffer = new byte[1024];  // buffer store for the stream
        int bytes; // bytes returned from read()

        // Keep listening to the InputStream until an exception occurs
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                // Send the obtained bytes to the UI activity
                mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                break;
            }
        }
    }

    /* Call this from the main activity to send data to the remote device */
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);
        } catch (IOException e) { }
    }

    /* Call this from the main activity to shutdown the connection */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

构造函数获取必要的流,一旦执行,线程将等待数据通过InputStream。当read(byte[])从流中返回字节时,使用父类中的成员处理程序将数据发送到主要活动。然后它返回并等待流中的更多字节。

发送传出数据就像write()从主活动调用线程的方法一样简单, 并传递要发送的字节。该方法然后简单地调用write(byte[])将数据发送到远程设备。

线程的cancel()方法很重要,因此可以随时关闭连接BluetoothSocket。当您完成使用蓝牙连接时,应始终调用此操作。

GitHub项目地址