失效链接处理 |
Android开发之蓝牙详解 PDF 下载
本站整理下载:
提取码:21zd
相关截图:
主要内容:
Android 开发之蓝牙详解 一.概述
这篇文章是我学习 Android 开发官网以及网上一些其他文章总结而来,主要就是为了好
好研究一下蓝牙开发,看完这篇文章以后,我们就知道了怎样使用蓝牙 API 完成建立蓝牙连
接的必要四步:1.打开蓝牙;2.查找附近已配对或可用的设备;3.连接设备;4.设备间数据
交换。由于文章比较长,为了方便大家的学习,所以将文章分为三篇,这是第一篇。
二.基础
1.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 和 HandsFree(v1.5)的配置。
BluetoothA2dp
定义如何把高品质的音频通过蓝牙连接从一个设备流向另一个设备。“A2DP”是
Advanced Audio Distribution Profile 的缩写。
BluetoothHealth
代表一个健康保健设备配置的控制蓝牙服务的代理。
BluetoothHealthCallback
用于实现 BluetoothHealth 回调的抽象类。你必须继承这个类,并实现它的回调方法,
来接收应用程序的注册状态和蓝牙通道状态变化的更新。
BluetoothHealthAppConfiguration
代表蓝牙相关的第三方健康保健应用程序所注册的与远程蓝牙健康保健设备进行通信
的配置。
BluetoothProfile.ServiceListener
BluetoothProfile IPC 客户端连接或断开服务的通知接口(它是运行特俗配置的内部
服务)。
2.权限
为了在你的应用程序中使用蓝牙功能,至少要声明两个蓝牙权限(BLUETOOTH 和
BLUETOOTH_ADMIN)中的一个。
为了执行任何蓝牙通信(如请求连接、接收连接和传输数据),你必须申请 BLUETOOTH
权限。
为了启动设备发现或维护蓝牙设置,你必须申请 BLUETOOTH_ADMIN 权限。大多数需要这
个权限的应用程序,仅仅是为能够发现本地的蓝牙设备。这个权限所授予的其他能力应该不
被使用,除非是电源管理的应用程序,它会在依据用户的请求来修改蓝牙设置。注意:如果
你使用了 BLUETOOTH_ADMIN 权限,那么必须要有 BLUETOOTH 权限。
在清单文件中声明如下权限:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
3.设置蓝牙
在应用程序能够利用蓝牙通道通信之前,需要确认设备是否支持蓝牙通信,如果支持,
要确保它是可用的。
如果不支持蓝牙,那么你应该有好的禁用所有蓝牙功能。如果支持蓝牙,但是被禁用的,
那么你要在不离开你的应用程序的情况下,请求用户启用蓝牙功能,这种设置要使用
BluetoothAdapter 对象,在以下两个步骤中完成。
(1)获得 BluetoothAdapter 对象
BluetoothAdapter 对象是所有蓝牙活动都需要的,要获得这个对象,就要调用静态的
getDefaultAdapter() 方 法 。 这 个 方 法 会 返 回 一 个 代 表 设 备 自 己 的 蓝 牙 适 配 器 的
BluetoothAdapter 对象。整个系统有一个蓝牙适配器,你的应用程序能够使用这个对象来
进行交互。如果 getDefaultAdapter()方法返回 null,那么该设备不支持蓝牙,你的处理也
要在此结束。例如:
BluetoothAdapter mBluetoothAdapter =BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter ==null){
// 设备不支持蓝牙
}
(2)启用蓝牙功能
接下来,你需要确保蓝牙是可用的。调用 isEnabled()方法来检查当前蓝牙是否可用。
如果这个方法返回 false,那么蓝牙是被禁用的。要申请启用蓝牙功能,就要调用带有
ACTION_REQUEST_ENABLE 操作意图的 startActivityForResult()方法。它会给系统设置发一
个启用蓝牙功能的请求(不终止你的应用程序)。例如:
if(!mAdapter.isEnabled()) {
Intent intent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE );
activity.startActivityForResult(intent, requestCode);
}
这时会显示一个请求用户启用蓝牙功能的对话框
如果用户点击允许,那么系统会开始启用蓝牙功能,完成启动过程(有可能失败),焦
点会返回给你的应用程序。传递给 startActivityForResult()方法的 requestCode 是一个
你的应用程序中定义的整数(它必须大于 0),系统会把它作为参数返回到你的
onActivityResult()回调实现中。
如果蓝牙功能启用成功,你的 Activity 会在 onActivityResult()回调中接收到
RESULT_OK 结果,如果蓝牙没有被启动(或者用户响应了拒绝),那么该结果编码是
RESULT_CANCELED。
可选地,你的应用程序还可以监听 ACTION_STATE_CHANGED 广播 Intent,无论蓝牙状态
何时改变,系统都会广播这个 Intent。这个广播包含的附加字段 EXTRA_STATE 和
EXTRA_PREVIOUS_STATE 中分别指明了新的和旧的蓝牙状态。这些附加字段中可能的值是:
STATE_TURNING_ON、STATE_ON、STATE_TURNING_OFF 和 STATE_OFF。监听这个广播对于在应
用程序运行时检测蓝牙的状态是有用的。
提示:启用可发现能力会自动启动蓝牙功能。如果你计划在执行蓝牙活动之前,要始终
启用设备的可发现机制,就可以跳过上面的步骤,详细请参考下文“启用蓝牙可发现”。
到此,我们先给出一个例子,让大家理解一下:
我们设置了四个按钮,分别用于判断是否支持蓝牙,蓝牙是否开启,开启蓝牙,关闭蓝
牙,由于使用的是模拟器,所以我们可以看到实验结果是当前设备不支持蓝牙,并且蓝牙未
开启,点击后面两个按钮没反应,如果是在真是设备上,我们可以进行开启蓝牙和关闭蓝牙
的操作,大家下来可以试一试,下面给出代码:
/**
* Created by lxn on 2016/3/4.
* 蓝牙管理类
*/
public class BluetoothController {
private BluetoothAdapter mAdapter;
public BluetoothController(){
mAdapter = BluetoothAdapter.getDefaultAdapter();
}
/**
* 判断当前设备是否支持蓝牙
* @return
*/
public boolean isSupportBluetooth(){
if(mAdapter!=null){
return true;
}
return false;
}
/**
* 获取蓝牙的状态
* @return
*/
public boolean getBluetoothStatus(){
if(mAdapter!=null){
return mAdapter.isEnabled();
}
return false;
}
/**
* 打开蓝牙
* @param activity
* @param requestCode
*/
public void turnOnBluetooth(Activity activity,int requestCode){
if(mAdapter!=null&&!mAdapter.isEnabled()) {
Intent intent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(intent, requestCode);
}
}
/**
* 关闭蓝牙
*/
public void turnOffBluetooth(){
if(mAdapter!=null&&mAdapter.isEnabled()) {
mAdapter.disable();
}
} }
public class MainActivity extends AppCompatActivity {
public static final int REQUESTCODE_OPEN = 1;
private BluetoothController mController = new BluetoothController();
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 1);
switch (state) {
case BluetoothAdapter.STATE_OFF:
Toast.makeText(MainActivity.this, " 蓝 牙 已 关 闭 ",
Toast.LENGTH_SHORT).show();
break;
case BluetoothAdapter.STATE_ON:
Toast.makeText(MainActivity.this, " 蓝牙已打开 ",
Toast.LENGTH_SHORT).show();
break;
case BluetoothAdapter.STATE_TURNING_ON:
Toast.makeText(MainActivity.this, " 正 在 打 开 蓝 牙 ",
Toast.LENGTH_SHORT).show();
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Toast.makeText(MainActivity.this, " 正 在 关 闭 蓝 牙 ",
Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(MainActivity.this, " 未知状态 ",
Toast.LENGTH_SHORT).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new
IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(receiver, filter);
}
public void click(View view) {
switch (view.getId()) {
case R.id.btnIsSupport:
boolean flag = mController.isSupportBluetooth();
Toast.makeText(this, "flag = " + flag,
Toast.LENGTH_SHORT).show();
break;
case R.id.btnIsTurnOn:
boolean isTurnOn = mController.getBluetoothStatus();
Toast.makeText(this, "isTurnOn" + isTurnOn,
Toast.LENGTH_SHORT).show();
break;
case R.id.btnTurnOn:
mController.turnOnBluetooth(this, REQUESTCODE_OPEN);
break;
case R.id.btnTrunOff:
mController.turnOffBluetooth();
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(resultCode==RESULT_OK){
Toast.makeText(this, "终于打开了", Toast.LENGTH_SHORT).show();
}
} }
4.查找设备
使用 BluetoothAdapter 对象,能够通过设备发现或查询已配对的设备列表来找到远程
的蓝牙设备。
设备发现是一个扫描过程,该过程搜索本地区域内可用的蓝牙设备,然后请求一些彼此
相关的一些信息(这个过程被叫做“发现”、“查询”或“扫描”)。但是,本地区域内的蓝牙
设备只有在它们也启用了可发现功能时,才会响应发现请求。如果一个设备是可发现的,那
么它会通过共享某些信息(如设备名称、类别和唯一的 MAC 地址)来响应发现请求。使用这
些信息,执行发现处理的设备能够有选择的初始化跟被发现设备的连接。
一旦跟远程的设备建立的首次连接,配对请求就会自动的被展现给用户。当设备完成配
对,相关设备的基本信息(如设备名称、类别和 MAC 地址)就会被保存,并能够使用蓝牙 API
来读取。使用已知的远程设备的 MAC 地址,在任何时候都能够初始化一个连接,而不需要执
行发现处理(假设设备在可连接的范围内)。
要记住配对和连接之间的差异。配对意味着两个设备对彼此存在性的感知,它们之间有
一个共享的用于验证的连接密钥,用这个密钥两个设备之间建立被加密的连接。连接意味着
当前设备间共享一个 RFCOMM 通道,并且能够被用于设备间的数据传输。当前 Android 蓝牙
API 在 RFCOMM 连接被建立之前,要求设备之间配对。(在使用蓝牙 API 初始化加密连接时,
配对是自动被执行的。)
以下章节介绍如何发现已配对的设备,或发现新的使用了可发现功能的设备。
注意:默认 Android 设备是不可发现的。用户能够通过系统设置在限定的时间内变成可
发现的设备,或者应用程序能够请求用户启用可发现性,而不离开应用程序。如何启用可发
现性,会在下文来讨论。
查询配对设备
在执行设备发现之前,应该先查询已配对的设备集合,来看期望的设备是否是已知的。
调用 getBondedDevices()方法来完成这件工作。这个方法会返回一个代表已配对设备的
BluetoothDevice 对象的集合。例如,你能够查询所有的配对设备,然后使用一个
ArrayAdapter 对象把每个已配对设备的名称显示给用户。
/**
* 获取已经配对的设备
*/
public Set<BluetoothDevice> getConnetedDevices() {
if (mAdapter != null && mAdapter.isEnabled()) {
return mAdapter.getBondedDevices();
}
return null;
}
Set<BluetoothDevice> connetedDevices = mController.getConnetedDevices();
for(BluetoothDevice device:connetedDevices){
adapter.add(device.getName()+"\n"+ device.getAddress());
listView.setAdapter(adapter);
} 从 BluetoothDevice 对象来初始化一个连接所需要的所有信息就是 MAC 地址。在这个
例子中,MAC 地址被作为 ArrayAdapter 的一部分来保存,并显示给用户。随后,该 MAC 地
址能够被提取用于初始化连接。
发现设备
简单的调用 startDiscovery()方法就可以开始发现设备。该过程是异步的,并且该方
法会立即返回一个布尔值来指明发现处理是否被成功的启动。通常发现过程会查询扫描大约
12 秒,接下来获取扫描发现的每个设备的蓝牙名称。
为了接收每个被发现设备的的信息,你的应用程序必须注册一个 ACTION_FOUND 类型的
广播接收器。对应每个蓝牙设备,系统都会广播 ACTION_FOUND 类型的 Intent。这个 Intent
会携带 EXTRA_DEVICE 和 EXTRA_CLASS 附加字段,这个两个字段分别包含了 BluetoothDevice
和 BluetoothClass 对象。例如,下列演示了你如何注册和处理设备发现时的广播:
//第一步:注册广播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);//不要忘了在 onDestory 中 unregister
第二部:开始发现
mController.startDiscovery();//开始搜索可发现的设备
//第三步:接收广播
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//发现了设备
if(BluetoothDevice.ACTION_FOUND.equals(action)){
Toast.makeText(MainActivity.this, " 发现设备 ",
Toast.LENGTH_SHORT).show();
//从 Intent 中获取设备的 BluetoothDevice 对象
BluetoothDevice device =
intent.getParcelableExtra( BluetoothDevice.EXTRA_DEVICE);
adapter.add(device.getName()+"\n"+ device.getAddress());
listView.setAdapter(adapter);
}
}
};
结果如下:
可以看到,我们已经把发现的设备展示到了 ListView 中。
警告:执行设备发现,对于蓝牙适配器来说是一个沉重的过程,它会消耗大量的资源。
一旦发现要连接设备,在尝试连接之前一定要确认用 cancelDiscovery()方法来终止发现操
作。另外,如果已经有一个跟设备的连接,那么执行发现会明显的减少连接的可用带宽,因
此在有连接的时候不应该执行发现处理。
Android 开发之蓝牙详解(二) 一.概述
在第一篇 Android 开发之蓝牙详解(一)中我主要介绍了一些理论性的知识以及比较基
础的操作,比如控制蓝牙的状态,查找设备,显示当前已连接的设备,今天我们看看其他的
一些操作。
二.基础
1.可发现模式
在上一篇最后,我们介绍了如何发现设备,但是有些时候,我们当前设备是不可被发现
的,也就是说即使我们打开了蓝牙其他设备也是无法搜索到的。所以我们要学学如何启用设
备的可发现性。如果要让本地设备可以被其他设备发现,那么就要调用
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);
效果图:
如图所示,申请用户启用设备的可发现模式时,会显示这样一个对话框。如果响应“与
喜怒”,那么设备的可发现模式会持续指定的时间,而且你的 Activity 会接收带有结果代码
等于可发现设备持续时间的 onActivityResult()回调方法的调用。如果用户响应“拒绝”
或有错误发生,则结果代码等于 RESULT_CANCELED.
注意:如果设备没有开启蓝牙功能,那么开启设备的可发现模式会自动开启蓝牙。
在可发现模式下,设备会静静的把这种模式保持到指定的时长。如果你想要在可发现模
式被改变时获得通知,那么你可以注册一个 ACTION_SCAN_MODE_CHANGED 类型的 Intent 广
播。这个 Intent 对象中包含了 EXTRA_SCAN_MODE 和 EXTRA_PREVIOUS_SCAN_MODE 附加字段,
它 们 会 分 别 告 诉 你 新 旧 扫 描 模 式 。 它 们 每 个 可 能 的 值 是 :
SCAN_MODE_CONNECTABLE_DISCOVERABLE,SCAN_MODE_CONNECTABLE 或 SCAN_MODE_NONE,它们
分别指明设备是在可发现模式下,还是不在可发现模式下但依然可接收连接,或者是不在可
发现模式下,不能接收连接。
下面是示例代码:
public class MainActivity extends Activity {
public static final int TURN = 1;
private BluetoothController mController = new BluetoothController();
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,1);
switch (mode){
case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Toast.makeText(MainActivity.this, " 现 在 是 可 发 现 模 式 ",
Toast.LENGTH_SHORT).show();
break;
case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Toast.makeText(MainActivity.this, "现在不是可发现模式,但是
可以连接", Toast.LENGTH_SHORT).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册广播,监听模式改变
IntentFilter filter = new
IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
registerReceiver(mReceiver,filter);
}
public void click(View view){
switch (view.getId()){
case R.id.turnOn:
mController.turnOnBluetooth(this, TURN);
break;
case R.id.canFind:
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//定义持续时间
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 10);
startActivity(discoverableIntent);
break;
}
} }
如果你要初始化跟远程设备的连接,你不需要启用设备的可现性。只有在你想要把你的
应用程序作为服务端来接收输入连接时,才需要启用可发现性,因为远程设备在跟你的设备
连接之前必须能够发现它。
2.连接设备
为了让两个设备上的两个应用程序之间建立连接,你必须同时实现服务端和客户端机制,
因为一个设备必须打开服务端口,同时另一个设备必须初始化跟服务端设备的连接(使用服
务端的 MAC 地址来初始化一个连接)。当服务端和客户端在相同的 RFCOMM 通道上有一个
BluetoothSocket 连接时,才能够被认为是服务端和客户端之间建立了连接。这时,每个设
备能够获得输入和输出流,并且能够彼此开始传输数据。
服务端设备和客户端设备彼此获取所需的 BluetoothSocket 的方法是不同的。服务端会
在接收输入连接的时候接收到一个 BluetoothSocket 对象。客户端会在打开跟服务端的
RFCOMM 通道时接收到一个 BluetoothSocket 对象。
一种实现技术是自动的准备一个设备作为服务端,以便在每个设备都会有一个服务套接
字被打开,并监听连接请求。当另一个设备初始化一个跟服务端套接字的连接时,它就会变
成一个客户端。另一种方法,一个设备是明确的”host”连接,并且根据要求打开一个服务
套接字,而其他的设备只是简单的初始化连接。
注意:如果两个设备之前没有配对,那么 Android 框架会在连接过程期间,自动的显示
一个配对请求通知或对话框给用户,如图 3 所示。因此在试图连接设备时,你的应用程序不
需要关心设备是否被配对。FRCOMM 的尝试性连接会一直阻塞,一直到用户成功的配对,或
者是因用户拒绝配对或配对超时而失败。
当你想要连接两个设备时,一个必须通过持有一个打开的 BluetoothServerSocket 对象
来作为服务端。服务套接字的用途是监听输入的连接请求,并且在一个连接请求被接收时,
提供一个 BluetoothSocket 连 接 对 象 。 在 从 BluetoothServerSocket 对象中获取
BluetoothSocket 时,BluetoothServerSocket 能够(并且也应该)被废弃,除非你想要接
收更多的连接。
以下是建立服务套接字和接收一个连接的基本过程。
1. 调 用 listenUsingRfcommWithServiceRecord(String, UUID) 方 法 来 获 得 一 个
BluetoothServerSocket 对象。该方法中的 String 参数是一个可识别的你的服务端的名称,
系统会自动的把它写入设备上的 Service Discovery Protocol(SDP)数据库实体(该名称
是任意的,并且可以简单的使用你的应用程序的名称)。UUID 参数也会被包含在 SDP 实体中,
并且是跟客户端设备连接的基本协议。也就是说,当客户端尝试跟服务端连接时,它会携带
一个它想要连接的服务端能够唯一识别的 UUID。只有在这些 UUID 完全匹配的情况下,连接
才可能被接收。
通过调用 accept()方法,启动连接请求。这是一个阻塞调用。只有在连接被接收或发生
异常的情况下,该方法才返回。只有在发送连接请求的远程设备所携带的 UUID 跟监听服务
套接字所注册的一个 UUID 匹配的时候,该连接才被接收。连接成功,accept()方法会返回
一个被连接的 BluetoothSocket 对象。
除非你想要接收其他连接,否则要调用 close()方法。该方法会释放服务套接字以及它
所占用的所有资源,但不会关闭被连接的已经有 accept()方法所返回的 BluetoothSocket
对象。跟 TCP/IP 不一样,每个 RFCOMM 通道一次只允许连接一个客户端,因此在大多数情况
下,在接收到一个连接套接字之后,立即调用 BluetoothServerSocket 对象的 close()方法
是有道理的。
accept()方法的调用不应该在主 Activity 的 UI 线程中被执行,因为该调用是阻塞的,
这会阻止应用程序的其他交互。通常在由应用程序所管理的一个新的线程中来使用
BluetoothServerSocket 对象或 BluetoothSocket 对象来工作。要终止诸如 accept()这样的
阻塞调用方法,就要从另一个线程中调用 BluetoothServerSocket 对象(或 BluetoothSocket
对象)的 close()方法,这时阻塞会立即返回。注意在 BluetoothServerSocket 或
BluetoothSocket 对象上的所有方法都是线程安全的。
示例
以下是一个被简化的接收连接请求的服务端组件:
public class AcceptThread extends Thread{
private BluetoothServerSocket mServerSocket;
public AcceptThread(){
BluetoothServerSocket tmp = null;
try {
tmp = mController.getmAdapter()
.listenUsingRfcommWithServiceRecord("BluetoothServer",
UUID.fromString(getPackageName()));
} catch (IOException e) {
e.printStackTrace();
}
mServerSocket = tmp;
}
@Override
public void run() {
super.run();
BluetoothSocket socket = null;
//不断监听直到返回连接或者发生异常
while (true){
try {
//启连接请求,这是一个阻塞方法,必须放在子线程
socket = mServerSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
//建立了连接
if(socket!=null){
//管理连接(在一个独立的线程里进行)
manageConnectedSocket(socket);
try {
mServerSocket.close();//关闭连接
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 取消正在监听的接口
*/
public void cancel(){
try {
mServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void manageConnectedSocket(BluetoothSocket socket){
}
在这个例子中,只希望有一个呼入连接,因此连接一旦被接收,并获取了一个
BluetoothSocket 对象,应用程序就会把获得的 BluetoothSocket 对象发送给一个独立的线
程,然后关闭 BluetoothServerSocket 对象并中断循环。
注意,在 accept()方法返回 BluetoothSocket 对象时,套接字已经是被连接的,因此
你不应该再调用像客户端那样调用 connect()方法了。
应用程序中的 manageConnectedSocket()方法是一个自定义方法,它会初始化用于传输
数据的线程。
通常,一旦你监听完成呼入连接,就应该关闭 BluetoothServerSocket 对象。在这个示
例中,close()方法是在获得 BluetoothSocket 对象之后被调用的。你可能还想要提供一个
公共方法,以便在你的线程中能够关闭你想要终止监听的服务套接字事件中的私有
BluetoothSocket 对象。
作为连接的客户端
为了初始化一个与远程设备(持有打开的服务套接字的设备)的连接,首先必须获取个
代表远程设备的 BluetoothDevice 对象。然后使用 BluetoothDevice 对象来获取一个
BluetoothSocket 对象,并初始化该连接。
以下是一个基本的连接过程:
1. 通过调用 BluetoothDevice 的 createRfcommSocketToServiceRecord(UUID)方法,
获得一个 BluetoothSocket 对象。这个方法会初始化一个连接到 BluetoothDevice 对象的
BluetoothSocket 对 象 。 传 递 给 这 个 方 法 的 UUID 参 数 必 须 与 服 务 端 设 备 打 开
BluetoothServerSocket 对象时所使用的 UUID 相匹配。在你的应用程序中简单的使用硬编
码进行比对,如果匹配,服务端和客户端代码就可以应用这个 BluetoothSocket 对象了。
通过调用 connect()方法来初始化连接。在这个调用中,为了找到匹配的 UUID,系统会
在远程的设备上执行一个 SDP 查询。如果查询成功,并且远程设备接收了该连接请求,那么
它会在连接期间共享使用 RFCOMM 通道,并且 connect()方法会返回。这个方法是一个阻塞
调用。如果因为某些原因,连接失败或连接超时(大约在 12 秒之后),就会抛出一个异常。
因为 connect()方法是阻塞调用,这个连接过程始终应该在独立与主 Activity 线程之
外的线程中被执行。
注意:在调用 connect()方法时,应该始终确保设备没有正在执行设备发现操作。如果
是在发现操作的过程中,那么连接尝试会明显的变慢,并且更像是要失败的样子。
示例
以下是初始化蓝牙连接线程的一个基本的例子:
public class ConnectThread extends Thread{
private BluetoothDevice mDevice;
private BluetoothSocket mSocket;
public ConnectThread(BluetoothDevice device){
BluetoothSocket temp = null;
mDevice = device;
try {
temp =
mDevice.createRfcommSocketToServiceRecord(UUID.fromString(getPackageName()));
} catch (IOException e) {
e.printStackTrace();
}
mSocket = temp;
}
@Override
public void run() {
super.run();
//取消搜索因为搜索会让连接变慢
mController.getmAdapter().cancelDiscovery();
try {
//通过 socket 连接设备,这是一个阻塞操作,知道连接成功或发生异常
mSocket.connect();
} catch (IOException e) {
//无法连接,关闭 socket 并且退出
try {
mSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
//管理连接(在独立的线程)
// manageConnectedSocket(mmSocket);
}
/**
* 取消正在进行的链接,关闭 socket
*/
public void cancel() {
try {
mSocket.close();
} catch (IOException e) { }
}
}
在建立连接之前要调用 cancelDiscovery()方法。在连接之前应该始终调用这个方法,
并且不用实际的检查蓝牙发现处理是否正在运行也是安全的(如果想要检查,调用
isDiscovering()方法)。
manageConnectedSocket()方法是一个应用程序中自定义的方法,它会初始化传输数据
的线程。
当使用完 BluetoothSocket 对象时,要始终调用 close()方法来进行清理工作。这样做
会立即关闭被连接的套接字,并清理所有的内部资源。
好了,这篇文章就到这里。大家仔细理解体会。下面给出源码的下载地址
客户端:http://download.csdn.net/detail/small_lee/9453107
服务端:http://download.csdn.net/detail/small_lee/9453105
|