Android蓝牙开发

http://blog.sina.com.cn/s/blog_9f7feef101014zn8.html

Android蓝牙开发

 

学习之前先了解两个基本概念:

1.RFCOMM协议: 

一个基于欧洲电信标准协会ETSI07.10规程的串行线性仿真协议。此协议提供RS232控制和状态信号,如基带上的损坏,CTS以及数据信号等,为上层业务(如传统的串行线缆应用)提供了传送能力。 

RFCOMM是一个简单传输协议,其目的是针对如何在两个不同设备上的应用之间保证一条完整的通信路径,并在它们之间保持一通信段。

RFCOMM是为了兼容传统的串口应用,同时取代有线的通信方式,蓝牙协议栈需要提供与有线串口一致的通信接口而开发出的协议。RFCOMM协议提供对基于L2CAP协议的串口仿真,基于ETSI07.10。可支持在两个BT设备之间同时保持高达60路的通信连接。

RFCOMM只针对直接互连设备之间的连接,或者是设备与网络接入设备之间的互连。通信两端设备必须兼容于RFCOMM协议,有两类设备:DTE (Data Terminal Endpoint,通信终端,如PCPRINTER)DCE (Data Circuit Endpoint,通信段的一部分,如Modem)。此两类设备不作区分。

 

2.MAC硬件地址

MAC(Medium/MediaAccess Control, 介质访问控制)MAC地址是烧录在NetworkInterfaceCard(网卡,NIC)里的.MAC地址,也叫硬件地址,是由48比特长(6字节),16进制的数字组成.0-23位叫做组织唯一标志符(organizationally unique,是识别LAN(局域网)节点的标识.24-47位是由厂家自己分配。其中第40位是组播地址标志位。网卡的物理地址通常是由网卡生产厂家烧入网卡的EPROM(一种闪存芯片,通常可以通过程序擦写),它存储的是传输数据时真正赖以标识发出数据的电脑和接收数据的主机的地址。

 

Android平台提供的蓝牙API去实现蓝牙设备之间的通信,蓝牙设备之间的通信主要包括了四个步骤:设置蓝牙设备、寻找局域网内可能或者匹配的设备、连接设备和设备之间的数据传输。以下是建立蓝牙连接的所需要的一些基本类:

BluetoothAdapter类:代表了一个本地的蓝牙适配器。是所有蓝牙交互的的入口点。利用它你可以发现其他蓝牙设备,查询绑定了的设备,使用已知的MAC地址实例化一个蓝牙设备和建立一个BluetoothServerSocket(作为服务器端)来监听来自其他设备的连接。

BluetoothDevice类:代表了一个远端的蓝牙设备,使用它请求远端蓝牙设备连接或者获取远端蓝牙设备的名称、地址、种类和绑定状态(其信息是封装在BluetoothSocket中)。

BluetoothSocket类:代表了一个蓝牙套接字的接口(类似于TCP中的套接字),是应用程序通过输入、输出流与其他蓝牙设备通信的连接点。

BlueboothServerSocket类:代表打开服务连接来监听可能到来的连接请求(属于server端),为了连接两个蓝牙设备必须有一个设备作为服务器打开一个服务套接字。当远端设备发起连接连接请求的时候,并且已经连接到了的时候,BlueboothServerSocket类将会返回一个BluetoothSocket

BluetoothClass类:描述了一个蓝牙设备的一般特点和能力。的只读属性集定义了设备的主、次设备类和一些相关服务。然而,并没有准确描述所有该设备所支持的蓝牙文件和服务,而是作为对设备种类来说的一个小小暗示。

要操作蓝牙,先要在AndroidManifest.xml里加入权限:

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

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

Android所有关于蓝牙开发的类都在android.bluetooth包下,只有8个类。常用的四个类如下所示:

1. BluetoothAdapter

蓝牙适配器,直到我们建立BluetoothSocket连接之前,都要不断操作它。

BluetoothAdapter里的方法很多,常用的有以下几个:

cancelDiscovery()取消发现,也就是说当我们正在搜索设备的时候调用这个方法将不再继续搜索

disable()关闭蓝牙

enable()打开蓝牙,这个方法打开蓝牙不会弹出提示,更多的时候我们需要问下用户是否打开,以下两行代码同样是打开蓝牙,但会提示用户:

Intentenabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enabler,reCode);  //同startActivity(enabler);

getAddress()获取本地蓝牙地址

getDefaultAdapter()获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdapter

getName()获取本地蓝牙名称

getRemoteDevice(String address)根据蓝牙地址获取远程蓝牙设备

getState()获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)

isDiscovering()判断当前是否正在查找设备,是则返回true

isEnabled()判断蓝牙是否打开,已打开返回true,否则返回false

listenUsingRfcommWithServiceRecord(String name,UUID uuid)根据名称,UUID创建并返回BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步

startDiscovery()开始搜索,这是搜索的第一步

 

2. BluetoothDevice

描述了一个蓝牙设备

createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket

这个类其他的方法,如getAddress()getName()等,同BluetoothAdapter

 

3. BluetoothServerSocket

如果去除了Bluetooth相信大家一定再熟悉不过了,既然是Socket,方法就应该都差不多,这个类一种只有三个方法

两个重载的accept()accept(int timeout)

两者的区别在于后面的方法指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行还有一点需要注意的是,这两个方法都返回一个BluetoothSocket,最后的连接也是服务器端与客户端的两个BluetoothSocket的连接

close()关闭

4. BluetoothSocket是客户端,跟BluetoothServerSocket相对

一共5个方法,不出意外,都会用到

close()关闭

connect()连接

getInptuStream()获取输入流

getOutputStream()获取输出流

getRemoteDevice()获取远程设备

这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备

 

 

下面说说具体的编程实现

1.启动蓝牙功能:

首先通过调用静态方法getDefaultAdapter()获取蓝牙适配器BluetoothAdapter,以后你就可以使用该对象了。如果返回为空,则无法继续执行了例如:

BluetoothAdapter mBluetoothAdapter BluetoothAdapter.getDefaultAdapter();

if (mBluetoothAdapter == null) {

// Device does not support Bluetooth

}

其次,调用isEnabled()来查询当前蓝牙设备的状态,如果返回为false,则表示蓝牙设备没有开启,接下来你需要封装一个ACTION_REQUEST_ENABLE请求到intent里面,调用startActivityForResult()方法使能蓝牙设备,例如:

if (!mBluetoothAdapter.isEnabled()) {

Intent enableBtIntent new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

}

 

2. 查找设备:

使用BluetoothAdapter类里的方法,你可以查找远端设备(大概十米以内)或者查询在你手机上已经匹配(或者说绑定)的其他手机了。当然需要确定对方蓝牙设备已经开启或者已经开启了被发现使能功能(对方设备是可以被发现的是你能够发起连接的前提条件)。如果该设备是可以被发现的,会反馈回来一些对方的设备信息,比如名字、MAC地址等,利用这些信息,你的设备就可以选择去向对方初始化一个连接。

如果你是第一次与该设备连接,那么一个配对的请求就会自动的显示给用户。当设备配对好之后,他的一些基本信息(主要是名字和MAC)被保存下来并可以使用蓝牙的API来读取。使用已知的MAC地址就可以对远端的蓝牙设备发起连接请求。

匹配好的设备和连接上的设备的不同点:匹配好只是说明对方设备发现了你的存在,并拥有一个共同的识别码,并且可以连接。连接上:表示当前设备共享一个RFCOMM信道并且两者之间可以交换数据。也就是是说蓝牙设备在建立RFCOMM信道之前,必须是已经配对好了的。

 

3. 查询匹配好的设备:

在建立连接之前你必须先查询配对好了的蓝牙设备集(你周围的蓝牙设备可能不止一个),以便你选取哪一个设备进行通信,例如你可以你可以查询所有配对的蓝牙设备,并使用一个数组适配器将其打印显示出来:

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 ListView

mArrayAdapter.add(device.getName() "\n" device.getAddress());

}

建立一个蓝牙连接只需要MAC地址就已经足够了。

 

4. 扫描设备:

扫描设备,只需要简单的调用startDiscovery()方法,这个扫描的过程大概持续是12秒,应用程序为了ACTION_FOUND动作需要注册一个BroadcastReceiver来接受设备扫描到的信息。对于每一个设备,系统都会广播ACTION_FOUND动作。例如:

// Create BroadcastReceiver for ACTION_FOUND

private final BroadcastReceiver mReceiver new BroadcastReceiver() {

public void onReceive(Context context, Intent intent) {

String action intent.getAction();

// When discovery finds 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 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()方法停止扫描。显然,如果你已经连接上一个设备,启动扫描会减少你的通信带宽。

 

5. 使能被发现:Enabling discoverability

如果你想使你的设备能够被其他设备发现,将ACTION_REQUEST_DISCOVERABLE动作封装在intent中并调用startActivityForResult(Intent, int)方法就可以了。将在不使你应用程序退出的情况下使你的设备能够被发现。缺省情况下的使能时间是120秒,当然你可以可以通过添加EXTRA_DISCOVERABLE_DURATION字段来改变使能时间(最大不超过300秒,这是出于对你设备上的信息安全考虑)。例如:

Intent discoverableIntent new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);

startActivity(discoverableIntent);

运行该段代码之后,系统会弹出一个对话框来提示你启动设备使能被发现(过程中如果你的蓝牙功能没有开启,系统会帮你开启),并且如果你准备对该远端设备发现一个连接,你不需要开启使能设备被发现功能,因为该功能只是在你的应用程序作为服务器端的时候才需要。



5. 连接设备:

在应用程序中,想建立两个蓝牙设备之间的连接,必须实现客户端和服务器端的代码(因为任何一个设备都必须可以作为服务端或者客户端)。一个开启服务来监听,一个发起连接请求(使用服务器端设备的MAC地址)。当他们都拥有一个蓝牙套接字在同一RFECOMM信道上的时候,可以认为他们之间已经连接上了。服务端和客户端通过不同的方式或其他们的蓝牙套接字。当一个连接监听到的时候,服务端获取到蓝牙套接字。当客户可打开一个FRCOMM信道给服务器端的时候,客户端获取到蓝牙套接字。

注意:在此过程中,如果两个蓝牙设备还没有配对好的,android系统会通过一个通知或者对话框的形式来通知用户。RFCOMM连接请求会在用户选择之前阻塞。如下图:Android蓝牙开发(整理)2

 

6. 服务端的连接:

当你想要连接两台设备时,一个必须作为服务端(通过持有一个打开的BluetoothServerSocket),目的是监听外来连接请求,当监听到以后提供一个连接上的BluetoothSocket给客户端,当客户端从BluetoothServerSocket得到BluetoothSocket以后就可以销毁BluetoothServerSocket,除非你还想监听更多的连接请求。

建立服务套接字和监听连接的基本步骤:

首先通过调用listenUsingRfcommWithServiceRecord(String, UUID)方法来获取BluetoothServerSocket对象,参数String代表了该服务的名称,UUID代表了和客户端连接的一个标识(128位格式的字符串ID,相当于PIN码),UUID必须双方匹配才可以建立连接。其次调用accept()方法来监听可能到来的连接请求,当监听到以后,返回一个连接上的蓝牙套接字BluetoothSocket。最后,在监听到一个连接以后,需要调用close()方法来关闭监听程序。(一般蓝牙设备之间是点对点的传输)

注意:accept()方法不应该放在主Acitvity里面,因为是一种阻塞调用(在没有监听到连接请求之程序就一直停在那里)。解决方法是新建一个线程来管理。例如:

private class AcceptThread extends Thread {

private final BluetoothServerSocket mmServerSocket;

public AcceptThread() {

// Use 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 theclient code

tmp mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);

catch (IOException e) }

mmServerSocket tmp;

}

public void run() {

BluetoothSocket socket null;

// Keep listening until exception occurs or socket is returned

while (true) {

try {

socket mmServerSocket.accept();

catch (IOException e) {

break;

}

// If connection was accepted

if (socket != null) {

// Do work to manage the connection (in separate thread)

manageConnectedSocket(socket);

mmServerSocket.close();

break;

}

}

}

 

public void cancel() {

try {

mmServerSocket.close();

catch (IOException e) }

}

}

 

7. 客户端的连接:

为了初始化一个与远端设备的连接,需要先获取代表该设备的一个BluetoothDevice对象。通过BluetoothDevice对象来获取BluetoothSocket并初始化连接具体步骤:

使用BluetoothDevice对象里的方法createRfcommSocketToServiceRecord(UUID)来获取BluetoothSocketUUID就是匹配码。然后,调用connect()方法来。如果远端设备接收了该连接,他们将在通信过程中共享RFFCOMM信道,并且connect()方法返回。例如:

private class ConnectThread extends Thread {

private final BluetoothSocket mmSocket;

private final BluetoothDevice mmDevice;

public ConnectThread(BluetoothDevice device) {

// Use temporary object that is later assigned to mmSocket,

// because mmSocket is final

BluetoothSocket tmp null;

mmDevice device;

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

mAdapter.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 separate thread)

manageConnectedSocket(mmSocket);

}

 

注意:conncet()方法也是阻塞调用,一般建立一个独立的线程中来调用该方法。在设备discover过程中不应该发起连接connect(),这样会明显减慢速度以至于连接失败。且数据传输完成只有调用close()方法来关闭连接,这样可以节省系统内部资源。

 

8. 管理连接(主要涉及数据的传输):

当设备连接上以后,每个设备都拥有各自的BluetoothSocket。现在你就可以实现设备之间数据的共享了。

1> 首先通过调用getInputStream()getOutputStream()方法来获取输入输出流。然后通过调用read(byte[]) write(byte[]).方法来读取或者写数据。

2> 实现细节:以为读取和写操作都是阻塞调用,需要建立一个专用线程来管理。

3> 

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;

}

}

}

 

public void write(byte[] bytes) {

try {

mmOutStream.write(bytes);

catch (IOException e) }

}

 

public void cancel() {

try {

mmSocket.close();

catch (IOException e) }

}

}

BluetoothChat 例程分析

Google 提供的关于 Bluetooth 开发的例程为 Bluetoothchat。除去配置及 UI定义等文件,主程序文件共三个:BluetoothChat.java 、 BluetoothChatService.java 以及 DeviceListActivity.java ,详细功能可见下面的描述。

Android蓝牙开发(整理)2

1.BluetoothChat.java

例程的主 Activity 。 onCreate() 得到本地 BluetoothAdapter 设备,检查是否支持。 onStart() 中检查是否启用蓝牙,并请求启用,然后执行 setupChat()。 setupChat() 中先对界面中的控件进行初始化增加点击监听器等,然创建 BluetoothChatService 对象,该对象在整个应用过程中存在,并执行蓝牙连接建立、消息发送接受等实际的行为。


2.BluetoothChatService.java

public synchronized void start() 

开启 mAcceptThread 线程,由于样例程序是仅 人的聊天过程,故之前先检测 mConnectThread 和 mConnectedThread 是否运行,运行则先退出这些线程。

public synchronized void connect(BluetoothDevice device) 

取消 CONNECTING 和 CONNECTED 状态下的相关线程,然后运行新的 mConnectThread 线程。

public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) 

开启一个 ConnectedThread 来管理对应的当前连接。之前先取消任意现存的 mConnectThread 、 mConnectedThread 、 mAcceptThread 线程,然后开启新  mConnectedThread ,传入当前刚刚接受的 socket 连接。最后通过 Handler 来通知 UI 连接 OK 

public synchronized void stop() :停止所有相关线程,设当前状态为 NONE 

public void write(byte[] out) :在 STATE_CONNECTED 状态下,调用 mConnectedThread 里的 write 方法,写入 byte 

private void connectionFailed() :连接失败的时候处理,通知 ui ,并设为 STATE_LISTEN 状态。

private void connectionLost() :当连接失去的时候,设为 STATE_LISTEN 状态并通知 UI 

内部类:

private class AcceptThread extends Thread :创建监听线程,准备接受新连接。使用阻塞方式,调用 BluetoothServerSocket.accept() 。提供 cancel 方法关闭 socket 

private class ConnectThread extends Thread :这是定义的连接线程,专门用来对外发出连接对方蓝牙的请求和处理流程。构造函数里通过 BluetoothDevice.createRfcommSocketToServiceRecord(),从待连接的 device 产生 BluetoothSocket. 然后在 run 方法中 connect ,成功后调用 BluetoothChatSevice 的 connected() 方法。定义 cancel() 在关闭线程时能够关闭相关 socket 

private class ConnectedThread extends Thread :这个是双方蓝牙连接后一直运行的线程。构造函数中设置输入输出流。 Run 方法中使用阻塞模式的 InputStream.read() 循环读取输入流, 然后 post 到UI 线程中更新聊天消息。也提供了 write() 将聊天消息写入输出流传输至对方,传输成功后回写入 UI 线程。最后 cancel() 关闭连接的 socket 

 

3.DeviceListActivity.java

该类包含 UI 和操作的 Activity 类,作用是得到系统默认蓝牙设备的已配对设备列表,以及搜索出的未配对的新设备的列表。然后提供点击后发出连接设备请求的功能。

 

BluetoothDevice 类,此为对应的远程蓝牙 Device

createRfcommSocketToServiceRecord() :创建该 Device 的 socket 

BluetoothSocket 

connect() :请求连接蓝牙。

getInputStream() :得到输入流,用于接收远程方信息。

getOutputStream() :得到输出流,发送给远程方的信息。

close() :关闭蓝牙连接。

InputStream 类:

read(byte[]) :以阻塞方式读取输入流。

OutputStream 类:

write(byte[]) :将信息写入该输出流,发送给远程有关Bluetooth的英文文档如下所示:

Bluetooth

The Android platform includes support for the Bluetooth network stack, which allows device to wirelessly exchange data with other Bluetooth devices. The application framework provides access to the Bluetooth functionality through the Android Bluetooth APIs. These APIs let applications wirelessly connect to other Bluetooth devices, enabling point-to-point and multipoint wireless features.

Using the Bluetooth APIs, an Android application can perform the following:

· Scan for other Bluetooth devices

· Query the local Bluetooth adapter for paired Bluetooth devices

· Establish RFCOMM channels

· Connect to other devices through service discovery

· Transfer data to and from other devices

· Manage multiple connections

All of the Bluetooth APIs are available in the android.bluetooth package. Here's summary of the classes and interfaces you will need to create Bluetooth connections:

Four major tasks necessary to communicate using Bluetooth: setting up Bluetooth, finding devices that are either paired or available in the local area, connecting devices, and transferring data between devices.

BluetoothAdapter

Represents the local Bluetooth adapter (Bluetooth radio). The BluetoothAdapter is the entry-point for all Bluetooth interaction. Using this, you can discover other Bluetooth devices, query list of bonded (paired) devices, instantiate BluetoothDevice using known MAC address, and create BluetoothServerSocket to listen for communications from other devices.

BluetoothDevice

Represents remote Bluetooth device. Use this to request connection with remote device through BluetoothSocket or query information about the device such as its name, address, class, and bonding state.

BluetoothSocket

Represents the interface for Bluetooth socket (similar to TCP Socket). This is the connection point that allows an application to exchange data with another Bluetooth device via InputStream and OutputStream.

BluetoothServerSocket

Represents an open server socket that listens for incoming requests (similar to TCP ServerSocket). In order to connect two Android devices, one device must open server socket with this class. When remote Bluetooth device makes connection request to the this device, the BluetoothServerSocket will return connected BluetoothSocket when the connection is accepted.

BluetoothClass

Describes the general characteristics and capabilities of Bluetooth device. This is read-only set of properties that define the device's major and minor device classes and its services. However, this does not reliably describe all Bluetooth profiles and services supported by the device, but is useful as hint to the device type.

BluetoothProfile

An interface that represents Bluetooth profile. Bluetooth profile is wireless interface specification for Bluetooth-based communication between devices. An example is the Hands-Free profile. For more discussion of profiles, see Working with Profiles

BluetoothHeadset

Provides support for Bluetooth headsets to be used with mobile phones. This includes both Bluetooth Headset and Hands-Free (v1.5) profiles.

BluetoothA2dp

Defines how high quality audio can be streamed from one device to another over Bluetooth connection. "A2DP" stands for Advanced Audio Distribution Profile.

BluetoothProfile.ServiceListener

An interface that notifies BluetoothProfile IPC clients when they have been connected to or disconnected from the service (that is, the internal service that runs particular profile).

Bluetooth Permissions

In order to use Bluetooth features in your application, you need to declare at least one of two Bluetooth permissions: BLUETOOTH and BLUETOOTH_ADMIN.

Declare the Bluetooth permission(s) in your application manifest file. For example:

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

Setting Up Bluetooth

1. Get the BluetoothAdapter

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // Device does not support Bluetooth
}

2. Enable Bluetooth

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

Finding Devices

Using the BluetoothAdapteryou can find remote Bluetooth devices either through device discovery or by querying the list of paired (bonded) devices.

Note: Android-powered devices are not discoverable by default. user can make the device discoverable for limited time through the system settings, or an application can request that the user enable discoverability without leaving the application. How to enable discoverability is discussed below.

Querying paired devices

Before performing device discovery, its worth querying the set of paired devices to see if the desired device is already known. To do so, call getBondedDevices()This will return Set ofBluetoothDevicerepresenting paired devices. For example, you can query all paired devices and then show the name of each device to the user, using an 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 ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

All that's needed from the BluetoothDevice object in order to initiate connection is the MAC address. In this example, it's saved as part of an ArrayAdapter that's shown to the user. The MAC address can later be extracted in order to initiate the connection.


Discovering devices

// Create BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds 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 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

Enabling discoverability

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

Android蓝牙开发(整理)4

Note: If Bluetooth has not been enabled on the device, then enabling device discoverability will automatically enable Bluetooth.

You do not need to enable device discoverability if you will be initiating the connection to remote device. Enabling discoverability is only necessary when you want your application to host server socket that will accept incoming connections, because the remote devices must be able to discover the device before it can initiate the connection.

 

Android蓝牙开发(整理)4

Connecting Devices

In order to create connection between your application on two devices, you must implement both the server-side and client-side mechanisms, because one device must open server socket and the other one must initiate the connection (using the server device's MAC address to initiate connection).

The server device and the client device each obtain the required BluetoothSocket in different ways. The server will receive it when an incoming connection is accepted. The client will receive it when it opens an RFCOMM channel to the server.

 

Note: If the two devices have not been previously paired, then the Android framework will automatically show pairing request notification or dialog to the user during the connection procedure, as shown in Figure 3. So when attempting to connect devices, your application does not need to be concerned about whether or not the devices are paired. Your RFCOMM connection attempt will block until the user has successfully paired, or will fail if the user rejects pairing, or if pairing fails or times out.

Android蓝牙开发(整理)4

Connecting as server

When you want to connect two devices, one must act as server by holding an open BluetoothServerSocketThe purpose of the server socket is to listen for incoming connection requests and when one is accepted, provide connected BluetoothSocketWhen the BluetoothSocket is acquired from theBluetoothServerSocketthe BluetoothServerSocket can (and should) be discarded, unless you want to accept more connections.

Here's the basic procedure to set up server socket and accept connection:

1. Get a BluetoothServerSocket by calling the listenUsingRfcommWithServiceRecord(String, UUID).

2. Start listening for connection requests by calling accept().

3. Unless you want to accept additional connections, call close().

The accept() call should not be executed in the main Activity UI thread because it is blocking call and will prevent any other interaction with the application. It usually makes sense to do all work with a BluetoothServerSocket or BluetoothSocket in new thread managed by your application. To abort blocked call such as accept()call close() on the BluetoothServerSocket (orBluetoothSocketfrom another thread and the blocked call will immediately return. Note that all methods on a BluetoothServerSocket or BluetoothSocket are thread-safe.

Example

Here's simplified thread for the server component that accepts incoming connections:


private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;
 
    public AcceptThread() {
        // Use 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 socket is returned
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // If connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }
 
    
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}

Connecting as client

Here's the basic procedure:

1.Using the BluetoothDeviceget a BluetoothSocket by calling createRfcommSocketToServiceRecord(UUID).

2. Initiate the connection by calling connect().

Note: You should always ensure that the device is not performing device discovery when you call connect()If discovery is in progress, then the connection attempt will be significantly slowed and is more likely to fail.

 

Example

Here is basic example of thread that initiates Bluetooth connection:


private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;
 
    public ConnectThread(BluetoothDevice device) {
        // Use temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;
 
        // Get 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 separate thread)
        manageConnectedSocket(mmSocket);
    }
 
    
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

Managing Connection

When you have successfully connected two (or more) devices, each one will have connected BluetoothSocketThis is where the fun begins because you can share data between devices. Using theBluetoothSocketthe general procedure to transfer arbitrary data is simple:

1. Get the InputStream and OutputStream that handle transmissions through the socket, via getInputStream() and getOutputStream()respectively.

2. Read and write data to the streams with read(byte[]) and write(byte[]).

Example

Here's an example of how this might look:


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;
            }
        }
    }
 
    
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);
        } catch (IOException e) { }
    }
 
    
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

BluetoothAdapter

Represents the local Bluetooth adapter (Bluetooth radio). The BluetoothAdapter is the entry-point for all Bluetooth interaction. Using this, you can discover other Bluetooth devices, query list of bonded (paired) devices, instantiate BluetoothDevice using known MAC address, and create BluetoothServerSocket to listen for communications from other devices.

BluetoothDevice

Represents remote Bluetooth device. Use this to request connection with remote device through BluetoothSocket or query information about the device such as its name, address, class, and bonding state.

BluetoothSocket

Represents the interface for Bluetooth socket (similar to TCP Socket). This is the connection point that allows an application to exchange data with another Bluetooth device via InputStream and OutputStream.

BluetoothServerSocket

Represents an open server socket that listens for incoming requests (similar to TCP ServerSocket). In order to connect two Android devices, one device must open server socket with this class. When remote Bluetooth device makes connection request to the this device, the BluetoothServerSocket will return connected BluetoothSocket when the connection is accepted.

BluetoothClass

Describes the general characteristics and capabilities of Bluetooth device. This is read-only set of properties that define the device's major and minor device classes and its services. However, this does not reliably describe all Bluetooth profiles and services supported by the device, but is useful as hint to the device type.

BluetoothProfile

An interface that represents Bluetooth profile. Bluetooth profile is wireless interface specification for Bluetooth-based communication between devices. An example is the Hands-Free profile. For more discussion of profiles, see Working with Profiles

BluetoothHeadset

Provides support for Bluetooth headsets to be used with mobile phones. This includes both Bluetooth Headset and Hands-Free (v1.5) profiles.

BluetoothA2dp

Defines how high quality audio can be streamed from one device to another over Bluetooth connection. "A2DP" stands for Advanced Audio Distribution Profile.

BluetoothProfile.ServiceListener

An interface that notifies BluetoothProfile IPC clients when they have been connected to or disconnected from the service (that is, the internal service that runs particular profile).

To get a BluetoothAdapter representing the local Bluetooth adapter, call the static getDefaultAdapter() method. Fundamentally, this is your starting point for all Bluetooth actions. Once you have the local adapter, you can get set of BluetoothDevice objects representing all paired devices with getBondedDevices()start device discovery with startDiscovery()or create a BluetoothServerSocket to listen for incoming connection requests with listenUsingRfcommWithServiceRecord(String, UUID).

Note: Most methods require the BLUETOOTH permission and some also require the BLUETOOTH_ADMIN permission.

To get a BluetoothDeviceuse BluetoothAdapter.getRemoteDevice(String) to create one representing device of known MAC address (which you can get through device discovery withBluetoothAdapteror get one from the set of bonded devices returned by BluetoothAdapter.getBondedDevices()You can then open a BluetoothSocket for communication with the remote device, using createRfcommSocketToServiceRecord(UUID).

Note: Requires the BLUETOOTH permission.

The interface for Bluetooth Sockets is similar to that of TCP sockets: Socket and ServerSocketOn the server side, use a BluetoothServerSocket to create listening server socket. When connection is accepted by the BluetoothServerSocketit will return new BluetoothSocket to manage the connection. On the client side, use single BluetoothSocket to both initiate an outgoing connection and to manage the connection.

To create a BluetoothSocket for connecting to known device, use BluetoothDevice.createRfcommSocketToServiceRecord()Then call connect() to attempt connection to the remote device. This call will block until connection is established or the connection fails.

BluetoothSocket is thread safe. In particular, close() will always immediately abort ongoing operations and close the socket.

Note: Requires the BLUETOOTH permission.

On the server side, use a BluetoothServerSocket to create listening server socket. When connection is accepted by the BluetoothServerSocketit will return new BluetoothSocket to manage the connection. On the client side, use single BluetoothSocket to both initiate an outgoing connection and to manage the connection.

To create listening BluetoothServerSocket that's ready for incoming connections, use BluetoothAdapter.listenUsingRfcommWithServiceRecord()Then call accept() to listen for incoming connection requests. This call will block until connection is established, at which point, it will return a BluetoothSocket to manage the connection. Once the BluetoothSocket is acquired, it's good idea to call close() on the BluetoothServerSocket when it's no longer needed for accepting connections. Closing the BluetoothServerSocket will not close the returned BluetoothSocket.

BluetoothServerSocket is thread safe. In particular, close() will always immediately abort ongoing operations and close the server socket.

Note: Requires the BLUETOOTH permission.

实例

一、 布局文件main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    >

<Button  

android:id="@+id/btn_bt"

    android:layout_width="fill_parent" 

    android:layout_height="wrap_content" 

    android:text="查询蓝牙匹配设备"

    />

<Button  

android:id="@+id/btn_find"

    android:layout_width="fill_parent" 

    android:layout_height="wrap_content" 

    android:text="可发现"

    />

<Button  

android:id="@+id/btn_scanning"

    android:layout_width="fill_parent" 

    android:layout_height="wrap_content" 

    android:text="扫描设备"

    />

</LinearLayout>

二、 响应事件的ActivityMainActivity

package cn.bt;

 

import java.util.Iterator;

import java.util.Set;

 

import android.app.Activity;

import android.bluetooth.BluetoothAdapter;

import android.bluetooth.BluetoothDevice;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.content.IntentFilter;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

 

public class MainActivity extends Activity {

Button btn_bt; //查询蓝牙匹配设备

Button btn_find; //可发现

Button btn_scanning; //扫描设备

BluetoothAdapter bluetoothAdapter; //本机的蓝牙设备

 

 

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

btn_bt (Button) this.findViewById(R.id.btn_bt);

btn_bt.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

BluetoothAdapter bluetoothAdapter BluetoothAdapter

.getDefaultAdapter(); //获取本机的蓝牙设备

if (bluetoothAdapter != null) //判断本机是否具有蓝牙设备,如果为null,则无蓝牙设备;如果不为空,则有蓝牙设备

System.out.println("本机拥有蓝牙设备!");

if (!bluetoothAdapter.isEnabled()) //蓝牙设备为开启,下面的intent主要是发出一个提示是否开启蓝牙设备

Intent intent new Intent(

BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivity(intent);

}

Set<BluetoothDevice> devices bluetoothAdapter

.getBondedDevices(); //获取远程的蓝牙设备集合

System.out.println("远程设备蓝牙个数:devices.size());

if (devices.size() 0) //将所有蓝牙设备打印出来

for (Iterator iterator devices.iterator(); iterator

.hasNext();) {

BluetoothDevice device (BluetoothDevice) iterator

.next();

System.out.println("远程设备蓝牙的地址:"

device.getAddress());

}

}

else {

System.out.println("没有蓝牙设备!");

}

}

});

 

btn_find (Button)this.findViewById(R.id.btn_find);

btn_find.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

//创建一个intent对象,并将参数action的值设置为BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE

Intent discoverableIntent new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

//将一个键值对存放到intent中,主要用于指定可见状态的时间,默认是120秒。如果指定的时间大于300秒,则为300秒;小于300秒,则为指定的时间

discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 500);

startActivity(discoverableIntent);

}

});

 

 

bluetoothAdapter BluetoothAdapter.getDefaultAdapter(); //获得本地蓝牙设备

btn_scanning (Button)this.findViewById(R.id.btn_scanning);

btn_scanning.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

bluetoothAdapter.startDiscovery(); //开始扫描远程蓝牙

}

});

//添加一个action过滤

IntentFilter filter new IntentFilter(BluetoothDevice.ACTION_FOUND); 

registerReceiver(mReceiver, filter); //注册广播

}

 

private final BroadcastReceiver mReceiver new BroadcastReceiver() {

//接收远程广播设备

@Override

public void onReceive(Context context, Intent intent) {

String action intent.getAction();

if(BluetoothDevice.ACTION_FOUND.equals(action)) {

BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

System.out.println("远程设备的mac地址:device.getAddress());

}

}

};

}

三、 增加关于蓝牙的权限

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

      package="cn.bt"

      android:versionCode="1"

      android:versionName="1.0">

    <uses-sdk android:minSdkVersion="8" />

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

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

 

    <application android:icon="@drawable/icon" android:label="@string/app_name">

        <activity android:name=".MainActivity"

                  android:label="@string/app_name">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>

</manifest>


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android蓝牙开发可以通过Android Bluetooth API来实现。使用Bluetooth API,Android应用可以执行以下操作: 1. 检查设备是否支持蓝牙功能: ```java BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { // 设备不支持蓝牙 } ``` 2. 打开蓝牙: ```java if (!bluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } ``` 3. 搜索蓝牙设备: ```java bluetoothAdapter.startDiscovery(); ``` 4. 监听蓝牙设备的发现: ```java BroadcastReceiver receiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 处理发现的蓝牙设备 } } }; IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(receiver, filter); ``` 5. 连接到蓝牙设备: ```java BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); BluetoothSocket socket = device.createRfcommSocketToServiceRecord(MY_UUID); socket.connect(); ``` 6. 通过蓝牙发送和接收数据: ```java OutputStream outputStream = socket.getOutputStream(); outputStream.write(data); InputStream inputStream = socket.getInputStream(); byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer); ``` 请注意,上述代码仅为示例,实际开发中可能需要处理异常、权限请求等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值