Android Classic Bluetooth Guide(上篇)

来源:互联网 发布:淘宝联盟没有代购推广 编辑:程序博客网 时间:2024/06/05 17:45

蓝牙(Bluetooth

Android平台包含对蓝牙网络协议栈的支持,此协议栈允许一台设备已无线方式,同其他蓝牙设备交换数据。通过Android Bluetooth APIs,应用框架提供对蓝牙功能的访问。这些APIs使应用程序无线连接到其他蓝牙设备,具有点对点和多点无线电特性。

使用蓝牙APIs,一个Android应用程序能够实现如下:

l 扫描其他蓝牙设备

l 查询本地蓝牙适配器用于配对蓝牙设备

l 建立RFCOMM信道

l 通过发现服务连接其他设备

l 同其他设备进行数据传输

l 管理多个连接

本文档描述如何使用Classic BluetoothClassic Bluetooth对于更多强耗电操作是一个正确的选择,例如在蓝牙设备之间的数据传输和通信。对于低功耗需求的蓝牙设备,Android 4.3API 18等级)引入用于支持蓝牙低功耗的API。查看BLE以了解更多。

基础

本文档描述了如何使用Android蓝牙APIs,去完成使用蓝牙进行通信所需要的四个主要任务:设置蓝牙,找到周围匹配的或者可用的设备,连接设备以及设备间传输数据。

android:bluetooth包中提供所有的蓝牙APIs。这里是对创建蓝牙连接所需的类和接口的总结。

1BluetoothAdapter

代表本地蓝牙适配器(蓝牙无线电)。BluetoothAdapter是所有蓝牙交互的入口点。使用它,你能够发现其它蓝牙设备,查询一系列匹配设备,使用已知的MAC地址实例化一个蓝牙设备(BluetoothDevice),创建BluetoothServerSocket侦听来自其他设备的通信请求。

(2)BluetoothDevice

代表一个远程蓝牙设备。通过一个BluetoothSocket,使用这个类能够请求同一个远程设备的一个连接,或者查询有关这个设备的信息,例如它的名字,地址,类型以及连接状态。

(3)BluetoothSocket

代表一个蓝牙套接字通信(类似于一个TCP套接字)的接口。这是允许一个应用同另外一个蓝牙设备,通过InputStreamOutputStream进行数据交换的连接点。

(4)BluetoothServerSocket

代表一个开放server socket(类似于一个TCP ServerSocket),用于侦听新来请求。为了连接两个Android设备,一个设备必须使用这个类来打开一个server socket。当一个远程蓝牙设备发出一个连接到此设备的请求,当连接被请求被接受,BluetoothServerSocket将返回一个已连接的BluetoothSocket

(5)BluetoothClass

描述一个蓝牙设备的通用属性和能力。这是一个只读的属性集合,定义了设备的主要和次要设备类和它的服务。然而,这并没有可靠地描述所有的蓝牙配置和设备所支持的服务,但是作为这个设备类型的一个提示是有用的。

(6)BluetoothProfile

代表一个蓝牙profile的一个接口。一个蓝牙profile是一个无线接口规格,用于基于蓝牙设备间的通信。一个示例就是Hands-Free profile

(7)BluetoothHeadset

为同移动手机一起使用的蓝牙头戴设备提供支持。这包括蓝牙头戴设备和免提profiles

(8)BluetoothA2dp

定义了通过一个蓝牙链接,能够从一个设备传输多么高质量的音频到另外一个设备。“A2DP”代表Advanced Audio Distribution Profile

(9)BluetoothHealth

代表一个医疗设备Profile代理,能够控制蓝牙服务。

(10)BluetoothHealthCallback

一个用于实现BluetoothHealth回调的抽象类。你必须继承这个类并且实现其中的回调方法来接收更新,这些更新包括应用注册状态改变以及蓝牙信道状态的改变。

(11)BluetoothHealthAppConfiguration

代表一个应用配置,蓝牙医疗第三方应用注册此配置,用于同一个远程蓝牙医疗设备进行通信。

(12)BluetoothProfile.ServiceListener

一个接口用于通知BluetoothProfile IPC客户端,当他们已经连接到服务或者从一个服务失去连接(即是,内在的服务运行一个特别的profile)。

蓝牙权限

为了在你的应用中使用蓝牙特性,你必须声明蓝牙权限BLUETOOTH。你需要这个权限来执行任何蓝牙通信,例如请求一个连接,接受一个连接以及传输数据。

如果你希望你的应用初始化设备发现或者操作蓝牙设置,你同样必须声明BLUETOOTH_ADMIN权限。大多数应用为了发现周围蓝牙设备的能力,单独地需要这个权限。由这个权限授予的其他能力不应该被使用,除非这个应用是一个超级管理者,能够根据用户请求修改蓝牙设置。注意:如果使用BLUETOOTH_ADMIN权限,则你必须同时拥有BLUETOOTH权限。

在你的manifest文件中声明蓝牙权限。例如:

<manifest>

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

</manifest>

设置蓝牙


Figure 1 开启蓝牙对话框

    在你的应用能够通过蓝牙通信之前,你需要去验证你的设备支持蓝牙,然后,确保蓝牙已经开启。

    如果蓝牙不被支持,则你应当优雅地禁止掉任何蓝牙特性。如果支持蓝牙但是没有启用,则你能够在不离开应用的情况下,请求应用开启蓝牙,这个设置通过使用BluetoothAdapter在两步内完成。

1> 得到BluetoothAdapter

所有的蓝牙Activity都需要BluetoothAdapter。为了得到BluetoothAdapter,调用静态方法getDefaultAdapter()。这个方法会返回一个BluetoothAdapter,代表拥有蓝牙适配器的这个设备。对于整个系统存在一个蓝牙适配器,通过使用这个对象,你的应用能够同蓝牙适配器进行交互。如果getDefaultAdapter()返回为null,则表示设备不支持蓝牙。例如:

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

If (mBluetoothAdapter == null){

    //Device does not support Bluetooth

}

2 > 启用蓝牙

下一步,你需要确保蓝牙已经启用。调用isEnabled()去检查蓝牙是否已经启用。如果方法返回为false,则蓝牙未被启用。为了请求启用蓝牙,调用startActivityForResult()并且携带action intent参数ACTION_REQUEST_ENABLE。这会发起一个请求,通过系统设置来开启蓝牙(同时不停止你的应用)。例如:

If(!mBluetoothAdapter.isEnable()){

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

}

会显示一个对话框请求用户权限来启用蓝牙,如图Figure 1所示。如果用户响应“yes”,则系统会开始启用蓝牙,并且一旦你的应用处理完毕,界面焦点将会回到你的应用。

传输给startActivityForResult()的常量REQUEST_ENABLE_BT是一个本地定义的整型(应当大于零),在你的onActivityResult()实现中,系统将这个整型作为requestCode参数返回给你。

如果蓝牙启动成功,在onActivityResult()回调中,你的Activity接收到RESULT_OK结果码。如果蓝牙由于某一个错误没有启动,则返回码为RESULT_CANCELED

另外,你的应用同样能够侦听intent广播ACTION_STATE_CHANGED,无论何时蓝牙状态已经改变,系统就会发送此广播。这个广播包含额外的参数EXTRA_STATEEXTRA_PREVIOUS_STATE, 分别包含新的和旧的蓝牙状态。这些额外的参数可能的值有STATE_TURNING_ONSTATE_ONSTATE_TURNING_OFF,以及STATE_OFF。监测蓝牙状态改变同时你的程序正在运行,这时侦听广播变得重要。

Tip:开启可发现性会自动启用蓝牙,如果你计划在执行蓝牙Activity之前,一贯的启用设备的可发现性,则你能够跳过以上以上两个步骤。

找到设备

使用BluetoothAdapter,你能够找到远程蓝牙设备,不论是通过设备查找还是通过查询已匹配设备列表。

设备查找是一个扫描过程,通过搜索周围具有蓝牙功能的设备,然后对每一个设备请求一些信息(这有时候指发现、查询、扫描)。然而只有当设备当前能够被查找,一个周围的蓝牙设备才会对查询请求做出响应。如果一个设备是可以查找到的,则它会通过共享一些信息来对查询请求做出响应,例如设备名字,类型以及它的唯一MAC地址。使用这些信息,执行查找的设备能够选择去初始化一个到此查找到的设备的连接。

一旦和一个远程设备第一次形成了一个连接,一个匹配请求自动展现给用户。当一个设备匹配的时候,关于这个设备的基本信息(例如设备名称,类型以及MAC地址)会被保存,并且能够通过使用Bluetooth APIs来读取。使用一个远程设备已知的MAC地址,在不需要执行查找的情况下,能够在任何时候使用它初始化一个连接(假定设备在范围内)。

谨记正在配对和正在连接之间是存在差别的。进行匹配意味着两个设备都提醒各自的存在,拥有一个共享的,能够用来授权的连接密钥,并且能够互相建立一个加密的连接。连接意味着当前设备间共享一个RFCOMM信道,并且能够互相传递数据。当前Android蓝牙APIs在一个RFCOMM连接建立之前,需要设备已经配对。(当你使用蓝牙APIs初始化一个加密连接的时候,匹配是自动被执行的。)

下面段落描述如何找到已经匹配的设备,或者使用设备查找找到新的设备。

NoteAndroid支持的设备默认是不能够被查找到的。通过系统设置,用户能够使设备在一个有限的时间内能够被查找到。或者在不离开当前应用的情况下,能够请求用户启用设备的可查找性。

查询匹配设备

在执行设备查找之前,查询已匹配设备集合,来确认期望设备是否已经已知是有价值的。为如此,调用getBondedDevices()。这会返回代表已匹配设备BluetoothDevices的一个集合。例如,你能够查询所有已匹配设备,然后使用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()来启动查找设备。这个处理过程是异步的,并且方法会立即返回一个boolean值,用于指示查找是否已经成功启动。查找过程通常包含一个大约十二秒的查询扫描操作,紧随着一个页面扫描每个已找到的设备去取出它的蓝牙名称。

为了接收关于每个找到设备的信息,你的应用必须为ACTION_FOUND intent注册一个BroadcastReceiver。对每个设备,系统将会广播ACTION_FOUND intent。这个intent携带额外的参数EXTRA_DEVICEEXTRA_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

Caution : 对于蓝牙适配器,执行设备查询是一个重度操作,将会消耗很多它的资源。一旦你发现了一个要去连接的设备,在你试图开始连接之前,确认你总是使用cancelDiscovery()来停止查找。同样,如果你已经和一台设备保持着一个连接,则执行查找会很大程度上削减这个连接的可用带宽,因此当你已经连接时,你不应当执行查找操作。

开启可见性

如果你希望本地设备对其他设备是可见的,调用startActivityForResult(intent, int),并且使用ACTION_REQUEST_DISCOVERABLE action Intent。这会通过系统设置发出一个开启可见性模式的请求(同时不会停止你的应用)。默认的,这个设备会在120秒内成为可被查找的。通过添加额外的Intent EXTRA_DISCOVERABLE_DURATION,你能够定义一个不同的持续时间。一个app能够设定的最大持续时间为3600秒,并且0意味着这个设备总是可以被查找到的。任何小于0并且大于3600的值都将自动设置为120秒。例如,如下程序片段设置持续时间为300

Intent discoverableIntent = new

Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);

startActivity(discoverableIntent);

Figure 2:开启可见性对话框

    会显示一个对话框,请求开启设备可见性的用户权限,如左图2所示。如果用户响应“Yes”,则设备在给定的时间内将会成为可被查找的。然后你的activity将会收到对onActivityResult()回调方法的调用。回调方法返回的result code和设备可见性的持续时间是相等的。如果用户响应“No.”或者如果发生了一个错误,则这个result code将会是RESULT_CANCELED

Note:如果在此设备上没有启用蓝牙,则启用设备可见性将会自动启用蓝牙。

设备在分配的时间内将会保持可被查找模式。当可被查找模式发生改变的时候,如果你想得到提醒,你可以为ACTION_SCAN_MODE_CHANGED intent注册一个BroadcastReceiver。这会包含额外的参数EXTRA_SCAN_MODE以及EXTRA_PREVIOUS_

SCAN_MODE,它们分别告诉你新的和旧的扫描模式。它们各自可能的值有SCAN_MODE_

CONNECTABLE_DISCOVERABLE, SCAN_MODE_CONNECTABLE,或者SCAN_MODE_

NONE,这表明这个设备在可见模式,或者不在可见模式但是依旧能够收到连接,或者不在可见模式并且不能够收到连接。

如果你将初始化到一个远程设备的连接,你不需要启用设备的可见性。只有当你希望你的应用保持一个server socket的时候,你才需要启用可见性,这个server socket用于接收新来到的连接,因为远程设备在能够初始化连接之前,它必须能够查找这个设备。

连接设备

为了能够在两台设备的应用之间创建一个连接,你必须实现客户端以及服务端的机制,因为一台设备必须打开一个server socket,同时另外一个必须初始化连接(使用服务端设备的MAC地址来初始化一个连接)。当两台设备在相同的RFCOMM信道中各有一个已连接的BluetoothSocket的时候,服务端和客户端被认为是互相连接的。在这个点上,每台设备能够获得输入和输出流并且开始数据传输,本节将介绍如何在两台设备之间初始化连接。

服务端和客户端以不同的方式各自获得所需的BluetoothSocket。当一个来到的连接被接受的时候,服务端将会收到BluetoothSocket。当客户端打开一个连接服务端的RFCOMM信道的时候,它会收到它的BluetoothSocket

一个实现技术就是自动将每个设备准备好作为一个服务端,因此每个设备都将打开一个server socket并且侦听连接。然后每个设备都能够初始化一个到另外一个设备的连接并且成为客户端。另外一方面,一个设备能够显示地保持住这个连接并且根据需要打开一个server socket,另外一个设备能够简单地初始化这个连接。

Figure 3:蓝牙配对对话框

Note:如果两台设备之前没有配对过,则在连接过程中,Android框架层会自动给用户显示一个配对请求提示或者对话框,如图3所示。因此当试图连接设备的时候,你的应用不需要考虑,你的设备是否已经配对过。在用户成功配对之前,你的RFCOMM连接请求会一直阻塞,如果用户拒绝配对的话则RFCOMM连接请求会失败,这也有可能是配对失败或者请求超时。

0 0
原创粉丝点击