Bluetooth 4.0之Android 解说

时间:2023-03-09 15:35:25
Bluetooth 4.0之Android 解说


Android平台包括了对蓝牙网络协议栈的支持,它同意一个蓝牙设备跟其它的蓝牙设备进行无线的数据交换。应用程序通过Android蓝牙API提供訪问蓝牙的功能。

这些API会把应用程序无线连接到其它的蓝牙设备上,具有点到点和多点无线特征。

使用蓝牙API,Android应用程序可以运行下面功能:

1.  扫描其它蓝牙设备

2.  查询本地已经配对的蓝牙适配器

3.  建立RFCOMM通道

4.  通过服务发现来连接其它设备

5.  在设备间数据传输

6.  管理多个蓝牙连接

基础

本文介绍怎样使用Android的蓝牙API来完毕使用蓝牙通信所须要的四项主要任务:设置蓝牙、查找已配对或区域内可用的蓝牙设备、连接设备、设备间数据传输。

全部的可用的API都在android.bluetooth包中。

下面概要的介绍创建蓝牙连接所须要的类和接口:

BluetoothAdapter

代表本地蓝牙适配器(蓝牙无线)。BluetoothAdapter是全部蓝牙交互的入口。

使用这个类。你可以发现其它的蓝牙设备,查询已配对设备的列表,使用已知的MAC地址来实例化一个BluetoothDevice对象,而且创建一个BluetoothServerSocket对象来监听与其它设备的通信。

BluetoothDevice

代表一个远程的蓝牙设备。使用这个类通过BluetoothSocket或查询诸如名称、地址、类和配对状态等设备信息来请求跟远程设备的连接。

BluetoothSocket

代表蓝牙socket的接口(类似TCP的Socket)。这是同意一个应用程序跟还有一个蓝牙设备通过输入流和输出流进行数据交换的连接点。

BluetoothServerSocket

代表一个打开的监听传入请求的服务接口(类似于TCP的ServerSocket)。

为了连接两个Android设备,一个设备必须用这个类打开一个服务接口。当远程蓝牙设备请求跟本设备建立连接请求时。BluetoothServerSocket会在连接被接收时返回一个被连接的BluetoothSocket对象。

BluetoothClass

描写叙述了蓝牙设备的一般性特征和功能。

这个类是一个仅仅读的属性集,这些属性定义了设备的主要和次要设备类和服务。可是,这个类并不保证描写叙述了设备所支持的全部的蓝牙配置和服务,可是这样的对设备类型的提示是故意的。

BluetoothProfile

    代表一个蓝牙配置的接口。

蓝牙配置是基于蓝牙通信的设备间的无线接口规范。一个样例是免提的配置。

很多其它的配置讨论,请看下文的用配置来工作。

BluetoothHeadset

    提供对使用蓝牙耳机的移动电话的支持。它同一时候包括了Bluetooth Headset和Hands-Free(v1.5)的配置。

BluetoothA2dp

    定义怎样把高品质的音频通过蓝牙连接从一个设备流向还有一个设备。“A2DP”是Advanced Audio Distribution Profile的缩写。

BluetoothHealth

    代表一个健康保健设备配置的控制蓝牙服务的代理。

BluetoothHealthCallback

    用于实现BluetoothHealth回调的抽象类。

你必须继承这个类,并实现它的回调方法,来接收应用程序的注冊状态和蓝牙通道状态变化的更新。

BluetoothHealthAppConfiguration

    代表蓝牙相关的第三方健康保健应用程序所注冊的与远程蓝牙健康保健设备进行通信的配置。

BluetoothProfile.ServiceListener

    BluetoothProfile IPCclient连接或断开服务的通知接口(它是执行特俗配置的内部服务)。

Android的联通性---Bluetooth(二)



蓝牙权限

为了在你的应用程序中使用蓝牙功能。至少要声明两个蓝牙权限(BLUETOOTH和BLUETOOTH_ADMIN)中的一个。

为了运行不论什么蓝牙通信(如请求连接、接收连接和数据传输)。你必须申请BLUETOOTH权限。

为了启动设备发现或维护蓝牙设置,你必须申请BLUETOOTH_ADMIN权限。大多数需要这个权限的应用程序,不过为可以发现本地的蓝牙设备。

这个权限所授予的其它能力应该不被使用。除非是电源管理的应用程序,它会在根据用户的请求来改动蓝牙设置。注意:假设你使用了BLUETOOTH_ADMIN权限,那么必需要有BLUETOOTH权限。

在你的应用程序清单文件里声明蓝牙权限。比如:

<manifest ... >

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

  ...

< /manifest>

关于声明应用程序权限的很多其它信息,请參阅<uses-permission>



设置蓝牙

在应用程序可以利用蓝牙通道通信之前,须要确认设备是否支持蓝牙通信。假设支持,要确保它是可用的。

假设不支持蓝牙,那么你应该有好的禁用全部蓝牙功能。假设支持蓝牙,可是被禁用的。那么你要在不离开你的应用程序的情况下。请求用户启用蓝牙功能,这样的设置要使用BluetoothAdapter对象,在下面两个步骤中完毕。

1. 获得BluetoothAdapter对象

BluetoothAdapter对象是全部蓝牙活动都须要的。要获得这个对象,就要调用静态的getDefaultAdapter()方法。

这种方法会返回一个代表设备自己的蓝牙适配器的BluetoothAdapter对象。

整个系统有一个蓝牙适配器,你的应用程序可以使用这个对象来进行交互。

假设getDefaultAdapter()方法返回null,那么该设备不支持蓝牙,你的处理也要在此结束。比如:

BluetoothAdapter mBluetoothAdapter =BluetoothAdapter.getDefaultAdapter();

if(mBluetoothAdapter ==null){

// Device does not support Bluetooth

}

2. 启用蓝牙功能

接下来,你须要确保蓝牙是可用的。调用isEnabled()方法来检查当前蓝牙是否可用。假设这种方法返回false。那么蓝牙是被禁用的。要申请启用蓝牙功能,就要调用带有ACTION_REQUEST_ENABLE操作意图的startActivityForResult()方法。它会给系统设置发一个启用蓝牙功能的请求(不终止你的应用程序)。

比如:

if(!mBluetoothAdapter.isEnabled()){

    Intent enableBtIntent =newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

}

这时会显示一个请求用户启用蓝牙功能的对话框,如图1所看到的:







图1.启用蓝牙功能的对话框。

假设用户响应“Yes”,那么系统会開始启用蓝牙功能,完毕启动过程(有可能失败),焦点会返回给你的应用程序。

传递给startActivityForResult()方法的REQUEST_ENABLE_BT常量,是一个你的应用程序中定义的整数(它必须大于0),系统会把它作为requestCode參数返回到你的onActivityResult()回调实现中。

假设蓝牙功能启用成功,你的Activity会在onActivityResult()回调中接收到RESULT_OK结果,假设蓝牙没有被启动(或者用户响应了“No”),那么该结果编码是RESULT_CANCELED。

可选地。你的应用程序还能够监听ACTION_STATE_CHANGED广播Intent。不管蓝牙状态何时改变,系统都会广播这个Intent。这个广播包括的附加字段EXTRA_STATE和EXTRA_PREVIOUS_STATE中分别指明了新的和旧的蓝牙状态。这些附加字段中可能的值是:STATE_TURNING_ON、STATE_ON、STATE_TURNING_OFF和STATE_OFF。监听这个广播对于在应用程序执行时检測蓝牙的状态是实用的。

提示:启用可发现能力会自己主动启动蓝牙功能。

假设你计划在运行蓝牙活动之前,要始终启用设备的可发现机制。就能够跳过上面的第2步,具体请參阅下文“启用蓝牙可发现”。



Android的联通性---Bluetooth(三)



查找设备

使用BluetoothAdapter对象。可以通过设备发现或查询已配对的设备列表来找到远程的蓝牙设备。

设备发现是一个扫描过程,该过程搜索本地区域内可用的蓝牙设备,然后请求一些彼此相关的一些信息(这个过程被叫做“发现”、“查询”或“扫描”)。可是,本地区域内的蓝牙设备仅仅有在它们也启用了可发现功能时,才会响应发现请求。假设一个设备是可发现的,那么它会通过共享某些信息(如设备名称、类别和唯一的MAC地址)来响应发现请求。使用这些信息,运行发现处理的设备可以有选择的初始化跟被发现设备的连接。

一旦跟远程的设备建立的首次连接。配对请求就会自己主动的被展现给用户。

当设备完毕配对,相关设备的基本信息(如设备名称、类别和MAC地址)就会被保存。并可以使用蓝牙API来读取。使用已知的远程设备的MAC地址。在不论什么时候都可以初始化一个连接,而不须要运行发现处理(如果设备在可连接的范围内)。

要记住配对和连接之间的差异。配对意味着两个设备对彼此存在性的感知。它们之间有一个共享的用于验证的连接密钥,用这个密钥两个设备之间建立被加密的连接。连接意味着当前设备间共享一个RFCOMM通道,而且可以被用于设备间的传输数据。当前Android蓝牙API在RFCOMM连接被建立之前,要求设备之间配对。

(在使用蓝牙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地址。在这个样例中。MAC地址被作为ArrayAdapter的一部分来保存,并显示给用户。随后,该MAC地址可以被提取用于初始化连接。

发现设备

简单的调用startDiscovery()方法就能够開始发现设备。该过程是异步的。而且该方法会马上返回一个布尔值来指明发现处理是否被成功的启动。通常发现过程会查询扫描大约12秒,接下来获取扫描发现的每一个设备的蓝牙名称。

为了接收每一个被发现设备的的信息,你的应用程序必须注冊一个ACTION_FOUND类型的广播接收器。相应每一个蓝牙设备,系统都会广播ACTION_FOUND类型的Intent。这个Intent会携带EXTRA_DEVICE和EXTRA_CLASS附加字段。这个两个字段分别包括了BluetoothDevice和BluetoothClass对象。比如,下列演示了你怎样注冊和处理设备发现时的广播:

// 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

警告:运行设备发现,对于蓝牙适配器来说是一个沉重的过程,它会消耗大量的资源。

一旦发现要连接设备,在尝试连接之前一定要确认用cancelDiscovery()方法来终止发现操作。另外,假设已经有一个跟设备的连接,那么运行发现会明显的降低连接的可用带宽,因此在有连接的时候不应该运行发现处理。

Android的联通性---Bluetooth(四)



启用设备的可发现性

假设要让本地设备能够被其它设备发现。那么就要调用ACTION_REQUEST_DISCOVERABLE操作意图的startActivityForResult(Intent, int)方法。这种方法会向系统设置发出一个启用可发现模式的请求(不终止应用程序)。

默认情况下,设备的可发现模式会持续120秒。通过给Intent对象加入EXTRA_DISCOVERABLE_DURATION附加字段,能够定义不同持续时间。

应用程序能够设置的最大持续时间是3600秒,0意味着设备始终是可发现的。不论什么小于0或大于3600秒的值都会自己主动的被设为120秒。比如,下面代码把持续时间设置为300秒:

Intent discoverableIntent = new

Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);

startActivity(discoverableIntent);

如图2所看到的,申请用户启用设备的可发现模式时。会显示这样一个对话框。假设响应“Yes”。那么设备的可发现模式会持续指定的时间,并且你的Activity会接收带有结果代码等于可发现设备持续时间的onActivityResult()回调方法的调用。假设用户响应“No”或有发生错误。则结果代码等于RESULT_CANCELED.

图2.启用可发现性对话框。

注意:假设设备没有开启蓝牙功能,那么开启设备的可发现模式会自己主动开启蓝牙。

在可发现模式下,设备会静静的把这样的模式保持到指定的时长。假设你想要在可发现模式被改变时获得通知,那么你能够注冊一个ACTION_SCAN_MODE_CHANGED类型的Intent广播。这个Intent对象中包括了EXTRA_SCAN_MODE和EXTRA_PREVIOUS_SCAN_MODE附加字段,它们会分别告诉你新旧扫描模式。它们每一个可能的值是:SCAN_MODE_CONNECTABLE_DISCOVERABLE。SCAN_MODE_CONNECTABLE或SCAN_MODE_NONE,它们分别指明设备是在可发现模式下,还是在可发现模式下但依旧可接收连接,或者是在可发现模式下并不能接收连接。

假设你要初始化跟远程设备的连接,你不须要启用设备的可现性。仅仅有在你想要把你的应用程序作为服务端来接收输入连接时。才须要启用可发现性,由于远程设备在跟你的设备连接之前必须可以发现它。

连接设备

为了让两个设备上的两个应用程序之间建立连接,你必须同一时候实现服务端和client机制,由于一个设备必须打开服务port。同一时候还有一个设备必须初始化跟服务端设备的连接(使用服务端的MAC地址来初始化一个连接)。

当服务端和client在同样的RFCOMM通道上有一个BluetoothSocket连接时。才可以被觉得是服务端和client之间建立了连接。这时。每一个设备可以获得输入和输出流。而且可以彼此開始数据传输。

服务端设备和client设备彼此获取所需的BluetoothSocket的方法是不同的。服务端会在接收输入连接的时候接收到一个BluetoothSocket对象。client会在打开跟服务端的RFCOMM通道时接收到一个BluetoothSocket对象。

一种实现技术是自己主动的准备一个设备作为服务端,以便在每一个设备都会有一个服务套接字被打开。并监听连接请求。当还有一个设备初始化一个跟服务端套接字的连接时,它就会变成一个client。还有一种方法,一个设备是明白的”host”连接。而且依据要求打开一个服务套接字,而其它的设备仅仅是简单的初始化连接。

注意:假设两个设备之前没有配对,那么Android框架会在连接过程期间,自己主动的显示一个配对请求通知或对话框给用户,如图3所看到的。因此在试图连接设备时,你的应用程序不须要关心设备是否被配对。FRCOMM的尝试性连接会一直堵塞,一直到用户成功的配对。或者是因用户拒绝配对或配对超时而失败。



图3.蓝牙配对对话框



Android的联通性---Bluetooth(五)

作为连接的服务端

当你想要连接两个设备时。一个必须通过持有一个打开的BluetoothServerSocket对象来作为服务端。服务套接字的用途是监听输入的连接请求,而且在一个连接请求被接收时,提供一个BluetoothSocket连接对象。

在从BluetoothServerSocket对象中获取BluetoothSocket时。BluetoothServerSocket可以(而且也应该)被废弃。除非你想要接收很多其它的连接。



下面是建立服务套接字和接收一个连接的基本过程。

1. 调用listenUsingRfcommWithServiceRecord(String, UUID)方法来获得一个BluetoothServerSocket对象。该方法中的String參数是一个可识别的你的服务端的名称,系统会自己主动的把它写入设备上的Service Discovery Protocol(SDP)数据库实体(该名称是随意的,而且可以简单的使用你的应用程序的名称)。

UUID參数也会被包括在SDP实体中,而且是跟client设备连接的基本协议。也就是说,当client尝试跟服务端连接时,它会携带一个它想要连接的服务端可以唯一识别的UUID。

仅仅有在这些UUID全然匹配的情况下,连接才可能被接收。



2. 通过调用accept()方法。启动连接请求。

这是一个堵塞调用。仅仅有在连接被接收或发生异常的情况下,该方法才返回。仅仅有在发送连接请求的远程设备所携带的UUID跟监听服务套接字所注冊的一个UUID匹配的时候,该连接才被接收。连接成功。accept()方法会返回一个被连接的BluetoothSocket对象。



3. 除非你想要接收其它连接,否则要调用close()方法。

该方法会释放服务套接字以及它所占用的全部资源,但不会关闭被连接的已经有accept()方法所返回的BluetoothSocket对象。

跟TCP/IP不一样。每一个RFCOMM通道一次仅仅同意连接一个client,因此在大多数情况下。在接收到一个连接套接字之后。马上调用BluetoothServerSocket对象的close()方法是有道理的。

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

    }

}

在这个样例中,仅仅希望有一个呼入连接,因此连接一旦被接收。并获取了一个BluetoothSocket对象,应用程序就会把获得的BluetoothSocket对象发送给一个独立的线程。然后关闭BluetoothServerSocket对象并中断循环。

注意。在accept()方法返回BluetoothSocket对象时,套接字已经是被连接的。因此你不应该再调用像client那样调用connect()方法了。

应用程序中的manageConnectedSocket()方法是一个自己定义方法,它会初始化用于数据传输的线程。

通常,一旦你监听完毕呼入连接,就应该关闭BluetoothServerSocket对象。在这个演示样例中,close()方法是在获得BluetoothSocket对象之后被调用的。你可能还想要提供一个公共方法,以便在你的线程中可以关闭你想要终止监听的服务套接字事件中的私有BluetoothSocket对象。

作为连接的client

为了初始化一个与远程设备(持有打开的服务套接字的设备)的连接。首先必须获取个代表远程设备的BluetoothDevice对象。

然后使用BluetoothDevice对象来获取一个BluetoothSocket对象,并初始化该连接。

下面是一个主要的连接过程:

1. 通过调用BluetoothDevice的createRfcommSocketToServiceRecord(UUID)方法。获得一个BluetoothSocket对象。

这种方法会初始化一个连接到BluetoothDevice对象的BluetoothSocket对象。传递给这种方法的UUID參数必须与服务端设备打开BluetoothServerSocket对象时所使用的UUID相匹配。在你的应用程序中简单的使用硬编码进行比对。假设匹配。服务端和client代码就能够应用这个BluetoothSocket对象了。

2. 通过调用connect()方法来初始化连接。在这个调用中。为了找到匹配的UUID,系统会在远程的设备上运行一个SDP查询。假设查询成功,而且远程设备接收了该连接请求。那么它会在连接期间共享使用RFCOMM通道,而且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 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()方法来进行清理工作。这样做会马上关闭被连接的套接字,并清理全部的内部资源。



Android的联通性---Bluetooth(六)



管理连接

当你成功的连接了两个(或很多其它)设备时。每个设备都有一个被连接的BluetoothSocket对象。这是良好的開始,由于你可以在设备之间共享数据。使用BluetoothSocket对象来传输随意数据的过程是简单的:

1. 分别通过getInputStream()和getOutputStream()方法来获得通过套接字来处理传输任务的InputStream和OutputStream对象。

2. 用read(byte[])和write(byte[])方法来读写流中的数据。

当然,有很多其它实现细节要考虑。首先和前提,对于全部数据流的读写应该使用专用的线程。

这是重要的,由于read(byte[])和write(byte[])方法是堵塞式调用。

Read(byte[])方法在从数据流中读取某些数据之前一直是堵塞的。write(byte[])方法一般是不堵塞的,可是对于流的控制。假设远程设备不是足够快的调用read(byte[])方法。而且中间缓存被填满,那么write(byte[])方法也会被堵塞。因此,你的线程中的主循环应该是专用于从InputStream对象中读取数据。

在线程类中有一个独立的公共方法用于初始化对OutputStream对象的写入操作。

演示样例

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[])方法从数据流中返回指定字节的数据时,使用来自其父类的Handler把该数据会被发送给主Activity,然后返回继续等待流中的很多其它的字节。

发送输出数据就像在主Activity中调用线程的write()方法一样简单,而且给该方法传入要发送的字节。

然后这种方法简单的调用write(byte[])方法把数据发送给远程设备。

线程的cancel()方法是至关重要的,以便该连接在不论什么时候可以通过关闭BluetoothSocket对象来终止。这种方法在使用完蓝牙连接时应该始终被调用。

使用配置来工作

从Android3.0開始,蓝牙API就包括了对用蓝牙配置来进行工作的支持。Bluetooth Profile是设备之间基于蓝牙进行通信的无线接口规范。

当中一个演示样例是Hands-Free配置。

对于一个连接到无线耳机的移动电话,移动电话和蓝牙耳机都必须支持Hands-Free配置。

你可以实现BluetoothProfile接口。来编写你自己的支持特殊蓝牙配置的类。Android蓝牙API提供了对下面蓝牙配置的实现:

· Headset(耳机):该配置提供了对用于移动电话的蓝牙耳机的支持。Android提供了BluetoothHeadset类。这个类是一个代理,它通过进程间通信(IPC)来控制蓝牙耳机服务。

它包括了Bluetoot Headset和Hands-Free(v1.5)配置。

BluetoothHeadset类包括了对AT命令的支持。

关于AT命令的很多其它讨论,请看“Vendor-specific AT commands”。

· A2DP:Advanced Audio Distribution Profile的缩写。

它定义怎样流化高品质的音频。并让音频流在通过蓝牙连接在设备之间传输。Android提供了BluetoothA2dp类,它一个通过IPC来控制的蓝牙A2DP服务的代理。

· Health Device:Android4.0(API Level 14)中引入了对Bluetooth Health Device Profile(HDP)的支持。

它可以让你创建一个跟支持蓝牙的健康保健设备进行蓝牙通信的应用程序,这些健康保健包含:心率监护仪、血压測量仪、体温计、体重秤等。

相应它支持的设备的列表和它们相应的设备数据规范,蓝牙联盟的站点:www.bluetooth.org。注意,这些值也是參考了ISO/IEEE11073-20601[7]规范中命名编码规范附件中的名为MDC_DEV_SPEC_PROFILE_*规范。

下面是使用配置进行工作的基本步骤

1. 获取默认的适配器。

2.使用getProfileProxy()方法来建立一个与配置相匹配的配置代理对象的连接。在以下的演示样例中。配置代理对象是一个BluetoothHeadset对象实例。

3.创建一个BluetoothProfile.ServiceListener监听器,该监听器会在它们连接到server或中断与server的连接时。通知BluetoothProfile的IPCclient。

4.在onServiceConnected()事件中。获得对配置代理对象的处理权;

5.一旦获得配置代理对象,就能够用它来监视连接的状态,并运行与配置有关的其它操作。

比如,下面代码片段显示怎样连接一个BluetoothHeadset代理对象。以便控制Headset配置:

BluetoothHeadset mBluetoothHeadset;



// Get the default adapter

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();



// Establish connection to the proxy.

mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);



private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {

    public void onServiceConnected(int profile, BluetoothProfile proxy) {

        if (profile == BluetoothProfile.HEADSET) {

            mBluetoothHeadset = (BluetoothHeadset) proxy;

        }

    }

    public void onServiceDisconnected(int profile) {

        if (profile == BluetoothProfile.HEADSET) {

            mBluetoothHeadset = null;

        }

    }

};



// ... call functions on mBluetoothHeadset



// Close proxy connection after use.

mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);



Android的联通性---Bluetooth(七)



健康设备配置

Android4.0(APILevel 14)中引入了对Bluetooth Health Device Profile(HDP)支持,这回让你创建跟支持蓝牙的健康设备进行蓝牙通信的应用程序,如心率监护仪、血压測量仪、体温计、体重秤等。

Bluetooth Health API包括了BluetoothHealth、BluetoothHealthCallbackhe和BluetoothHealthAppConfiguration等类。

在使用的Bluetooth Health API中。有助于理解下面关键的HDP概念:

概念

介绍

Source

HDP中定义的一个角色,一个Source是一个把医疗数据(如体重、血弹、体温等)传输给诸如Android手机或平板电脑等的设备。

Sink

HDP中定义的一个角色,在HDP中。一个Sink是一个接收医疗数据的小设备。

在一个Android HDP应用程序中。Sink用BluetoothHealthAppConfiguration对象来代表。

Registration

指的是给特定的健康设备注冊一个Sink。

Connection

指的是健康设备和Android手机或平板电脑之间打开的通信通道。

创建HDP应用程序

下面是创建Android HDP应用中所涉及到的基本步骤:

1. 获得BluetoothHealth代理对象的引用。

类似于常规的耳机和A2DP配置设备,必须调用带有BluetoothProfile.ServiceListener和HEALTH配置类型參数的getProfileProxy()方法来建立与配置代理对象的连接。

2. 创建BluetoothHealthCallback对象。并注冊一个扮演Health Sink角色的应用程序配(BluetoothHealthAppConfiguration)。

3. 建立跟健康设备的连接。某些设备会初始化连接,在这种设备中进行这一个步是没有必要的。

4. 当成功的连接到健康设备时,就能够使用文件描写叙述来读写健康设备。所接收到的数据须要使用健康管理器来解释。这个管理器实现了IEEE 11073-xxxxx规范。

5. 完毕以上步骤后。关闭健康通道,并注销应用程序。

该通道在长期被闲置时,也会被关闭。