Android蓝牙
前言:最近,新换了一家公司,公司的软件需要通过蓝牙与硬件进行通讯,于是趁此机会将Android蓝牙详细的了解了一下。本篇内容是基于普通蓝牙。
Android系统已经为我们提供了操作蓝牙的API,我们只要通过这些API便可以操控蓝牙,实现打开蓝牙设备,搜索周围蓝牙设备,与已连接的设备进行数据传输等操作。
阅读本文后你将会有一下收获
- 知道怎样打开手机蓝牙。
- 知道怎样获取已经进行蓝牙配对过的设备。
- 知道怎样进行设备之间的连接以及通讯。
- 知道怎样设置蓝牙设备可进行搜索到以及设置可被搜索的时长
一、蓝牙操作
打开手机蓝牙
设置蓝牙权限
要在应用中使用蓝牙功能,必须声明蓝牙权限 BLUETOOTH
。您需要此权限才能执行任何蓝牙通信,例如请求连接、接受连接和传输数据等。设置权限的代码如下
<uses-permission android:name="android.permission.BLUETOOTH" />
判断是否支持蓝牙
在打开手机蓝牙之前首先判断手机是否支持蓝牙,判断是否支持蓝牙的代码如下
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this,"当前设备不支持蓝牙!",Toast.LENGTH_SHORT).show();
}
解释一下BluetoothAdapter的作用
BluetoothAdapter代表本地设备蓝牙适配器,BluetoothAdapter可让您执行基本的蓝牙任务,如启动设备发现,查询绑定(配对)设备列表,使用已知的MAC地址实例化 BluetoothDevice,并创建 BluetoothServerSocket以侦听来自其他设备的连接请求,并开始扫描蓝牙LE设备。
如果设备支持蓝牙,则进行打开蓝牙的操作
打开蓝牙
调用 BluetoothAdapter的isEnabled()
方法来检查当前是否已启用蓝牙。 如果此方法返回 false,则表示蓝牙处于停用状态。想要启用蓝牙,则需要设置Intent的Action为ACTION_REQUEST_ENABLE
,然后通过startActivityForResult()
来启动蓝牙。具体的代码如下
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
这段代码执行完后,手机则会弹出是否允许开启蓝牙的提示框,如下图
当用户点击“拒绝”或则“允许”的时候 Activity 将会在 onActivityResult()
回调中收到结果代码。
当成功开启蓝牙时 Activity 将会在 onActivityResult()
回调中收到 RESULT_OK
结果代码。 如果由于某个错误(或用户响应“拒绝”)而没有启用蓝牙,则结果代码为 RESULT_CANCELED
。我们便可以重写 onActivityResult()
方法来判断蓝牙是否已经成功开启。
查询设备
查询已经配对的设备
在搜索设备之前,我们应该先查找已经进行配对的设备,如果目标设备已经进行过配对,则不需要进行设备搜索。因为,执行设备发现对于蓝牙适配器而言是一个非常繁重的操作过程,并且会消耗大量资源。可以通过BluetoothAdapter的getBondedDevices()
方法来查询已经配对的设备,具体代码如下
private void checkAlreadyConnect() {
//获取已经配对的集合
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
mArrayAdapter.notifyDataSetChanged();
}
}
搜索设备
要搜索周围的设备,只需调用BluetoothAdapter 的startDiscovery()
方法即可。
注:搜索设备是在异步进程中,通常会有12秒的时间来进行查询扫描,之后对每台发现的设备进行页面扫描,以检索其蓝牙名称。
调用startDiscovery()
方法的时候还需要新增如下权限
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
在发现设备后系统会进行ACTION_FOUND
的广播,因此,我们需要一个广播接收者来接收广播,以下代码为发现设备后如何注册来处理广播
// 新建一个 BroadcastReceiver来接收ACTION_FOUND广播
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// 发现设备
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
//获得 BluetoothDevice
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//向mArrayAdapter中添加设备信息
mSearchAdapter.add(device.getName() + "\n" + device.getAddress());
mSearchAdapter.notifyDataSetChanged();
}
}
};
//设置IntentFilter
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
注意:执行设备发现对于蓝牙适配器而言是一个非常繁重的操作过程,并且会消耗大量资源。 在找到要连接的设备后,确保始终使用
cancelDiscovery()
停止发现,然后再尝试连接。不应该在处于连接状态时执行发现操作。
启用可检测性
Android 设备默认处于不可检测到状态。 用户可通过系统设置将设备设为在有限的时间内处于可检测到状态,或者,应用可请求用户在不离开应用的同时启用可检测性。
如果您希望将本地设备设为可被其他设备检测到,请使用 ACTION_REQUEST_DISCOVERABLE
操作 Intent 调用 startActivityForResult(Intent, int)
。 这将通过系统设置发出启用可检测到模式的请求(无需停止您的应用)。 默认情况下,设备将变为可检测到并持续 120 秒钟。 您可以通过添加EXTRA_DISCOVERABLE_DURATION
Intent Extra 来定义不同的持续时间。 应用可以设置的最大持续时间为 3600 秒,值为 0 则表示设备始终可检测到。 任何小于 0 或大于 3600 的值都会自动设为 120 秒。 例如,以下片段会将持续时间设为 300 秒:
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
进行设备连接
引用官方的文档
要在两台设备上的应用之间创建连接,必须同时实现服务器端和客户端机制,因为其中一台设备必须开放服务器套接字,而另一台设备必须发起连接(使用服务器设备的 MAC 地址发起连接)。 当服务器和客户端在同一 RFCOMM 通道上分别拥有已连接的
BluetoothSocket
时,二者将被视为彼此连接。
由上面的引用可知,要想创建两个应用之间的连接,必须同时实现服务器端和客户端机制,下面,分别介绍怎样实现为服务器端和客户端机制。
实现为服务器
当您需要连接两台设备时,其中一台设备必须通过保持开放的
BluetoothServerSocket
来充当服务器。 服务器套接字的用途是侦听传入的连接请求,并在接受一个请求后提供已连接的BluetoothSocket
。 从BluetoothServerSocket
获取BluetoothSocket
后,可以(并且应该)舍弃BluetoothServerSocket
,除非您需要接受更多连接。
下面是作为服务端的代码
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null;
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME,
MY_UUID);
} catch (IOException e) {
}
mmServerSocket = tmp;
mState = STATE_LISTEN;
}
public void run() {
setName("AcceptThread");
BluetoothSocket socket = null;
while (mState != STATE_CONNECTED) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
if (socket != null) {
synchronized (BluetoothService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_CONNECTED:
try {
socket.close();
} catch (IOException e) {
}
break;
}
}
}
}
}
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) {
}
}
}
这里解释一下,为什么要新开一个线程来作为服务端,因为通过调用 accept()
这是一个阻塞调用。
注意:mAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID);中的MY_UUID必须与客服端连接时的UUID一致。
总结一下作为服务端的步骤:
- 通过调用
listenUsingRfcommWithServiceRecord(String, UUID)
获取BluetoothServerSocket
。 - 通过调用
accept()
开始侦听连接请求。 - 如果不想让更多的设备连接,则在连接后调用
close()
关闭。
实现为客户端
先看下作为客户端的代码
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(
MY_UUID);
} catch (IOException e) {
}
mmSocket = tmp;
mState = STATE_CONNECTING;
}
public void run() {
setName("ConnectThread");
mAdapter.cancelDiscovery();
try {
mmSocket.connect();
} catch (IOException e) {
try {
mmSocket.close();
} catch (IOException e2) {
}
return;
}
synchronized (BluetoothService.this) {
mConnectThread = null;
}
connected(mmSocket, mmDevice);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
}
}
这里有一点需要注意,也是官方文档中强调的
注:在调用
connect()
时,应始终确保设备未在执行设备发现。 如果正在进行发现操作,则会大幅降低连接尝试的速度,并增加连接失败的可能性。
因此,在进行连接之前必须调用 mAdapter.cancelDiscovery();
来关闭查找设备。这里总结一下作为客户端的步骤:
- 使用
BluetoothDevice
,通过调用createRfcommSocketToServiceRecord(UUID)
获取BluetoothSocket
。(这里的UUID必须与服务端的保持一致) - 通过调用
connect()
发起连接。
如果两台设备之前尚未配对,则在连接过程中,Android 框架会自动向用户显示配对请求通知或对话框,如下图所示。
进行配对之后就可以进行通信及数据的传输了。
数据传输
通过上面的几步,这时已经可以实现设备之间的连接了。下面说一下设备之间的通信。
其实在两台设备连接成功后,每台设备都会有一个已连接的 BluetoothSocket
。我们则可以利用 BluetoothSocket
,来进行数据的传输。看代码
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;
// 获取BluetoothSocket的input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
mState = STATE_CONNECTED;
}
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
while (mState == STATE_CONNECTED) {
try {
bytes = mmInStream.read(buffer);
Log.d(TAG, "已经连接");
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
break;
}
}
}
//写数据
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
//发送消息到主线程
mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
从上面代码中可以看到,首先是通过BluetoothSocket来拿到InputStream和OutputStream,然后利用read
,write
方法来读取和写入数据。
二、项目结构
三、结束语
本文的内容是基于普通蓝牙进行描述的,主要讲解了怎样操作蓝牙及进行设备间的通讯。不过现在好多都是在BLE蓝牙设备间进行通讯了,当然,我也会针对BLE蓝牙设备在写一篇文章,本文就是为后面的BLE蓝牙讲解做准备的。
转载请注明出处:www.wizardev.com
Android蓝牙
注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权
Android蓝牙的更多相关文章
- android蓝牙打印机
您还未登录!|登录|注册|帮助 首页 业界 移动 云计算 研发 论坛 博客 下载 更多 reality_jie的专栏 编程的过程是一种微妙的享受 目录视图 摘要视图 订阅 CSDN2013 ...
- Android蓝牙实例(和单片机蓝牙模块通信)
最近做毕设,需要写一个简单的蓝牙APP进行交互,在网上也找了很多资料,终于给搞定了,这里分享一下^_^. 1.Android蓝牙编程 蓝牙3.0及以下版本编程需要使用UUID,UUID是通用唯一识别码 ...
- Android 蓝牙4.0 BLE
Android ble (Bluetooth Low Energy) 蓝牙4.0,也就是说API level >= 18,且支持蓝牙4.0的手机才可以使用. BLE是蓝牙4.0的核心Profil ...
- android 蓝牙4.0 开发介绍
最近一直在研究一个蓝牙功能 由于本人是菜鸟 学起来比较忙 一直搞了好久才弄懂 , 网上对蓝牙4.0也就是几个个dome 抄来抄去,全是英文注解 , 对英语不好的朋友来说 真是硬伤 , 一些没必要的描 ...
- 【转】android蓝牙开发---与蓝牙模块进行通信--不错
原文网址:http://www.cnblogs.com/wenjiang/p/3200138.html 近半个月来一直在搞android蓝牙这方面,主要是项目需要与蓝牙模块进行通信.开头的进展很顺利, ...
- Android 蓝牙开发(整理大全)
Android蓝牙开发 鉴于国内Android蓝牙开发的例子很少,以及蓝牙开发也比较少用到,所以找的资料不是很全. (一): 由于Android蓝牙的通信都需要用到UUID,如果由手机发起搜索,当搜索 ...
- android -- 蓝牙 bluetooth (四)OPP文件传输
在前面android -- 蓝牙 bluetooth (一) 入门文章结尾中提到了会按四个方面来写这系列的文章,前面已写了蓝牙打开和蓝牙搜索,这次一起来看下蓝牙文件分享的流程,也就是蓝牙应用opp目录 ...
- android -- 蓝牙 bluetooth (三)搜索蓝牙
接上篇打开蓝牙继续,来一起看下蓝牙搜索的流程,触发蓝牙搜索的条件形式上有两种,一是在蓝牙设置界面开启蓝牙会直接开始搜索,另一个是先打开蓝牙开关在进入蓝牙设置界面也会触发搜索,也可能还有其它触发方式,但 ...
- android -- 蓝牙 bluetooth (一) 入门
前段时间在 网上看了一些关于android蓝牙的文章,发现大部分是基于老版本(4.1以前含4.1)的源码,虽然无碍了解蓝牙的基本原理和工作流程,但对着4.2.2的代码看起来总是有些遗憾.所以针对4.2 ...
- 深入了解Android蓝牙Bluetooth——《基础篇》
什么是蓝牙? 也可以说是蓝牙技术.所谓蓝牙(Bluetooth)技术,实际上是一种短距离无线电技术,是由爱立信公司公司发明的.利用"蓝牙"技术,能够有效地简化掌上电脑.笔记本电 ...
随机推荐
- 【linux高级程序设计】(第十五章)UDP网络编程应用 5
域名与IP信息解析 /etc/hosts 文件中有部分IP地址与域名主机名的信息 /etc/resolv.conf 里面有DNS服务器的IP地址 struct hostent { char *h_na ...
- poj 3164(最小树形图模板)
题目链接:http://poj.org/problem?id=3164 详细可以看这里:http://www.cnblogs.com/vongang/archive/2012/07/18/259685 ...
- springBoot 微服务
微服务,是OO (面向对象,Object Oriented) 专家 Martin Fowler 于2014年在他一篇文章<Microservice>提出的.在 Mattin 的头脑中,兴奋 ...
- phpstudy无法访问主页,提示You don't have permission to access / on this server解决办法
1.输入localhost提示:You don't have permission to access / on this server. 新版phpStudy为了安全,取消Apache和nginx列 ...
- Bug预防体系
Web常见产品问题及预防 测试人员在每次版本迭代中,会对项目的整体质量有一个把控,对于项目常见的问题,开发经常犯的错误都会有所了解,为了避免或者减少这样的错误或不规范的事情在发生,测试人员可以整理构建 ...
- luogu P1347 排序
题目描述 一个不同的值的升序排序数列指的是一个从左到右元素依次增大的序列,例如,一个有序的数列A,B,C,D 表示A<B,B<C,C<D.在这道题中,我们将给你一系列形如A<B ...
- [BZOJ 2957]楼房重建(THU2013集训)(线段树维护单调栈)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2957 分析: 根据题意,就是比较斜率大小 只看一段区间的话,那么这段区间能看见的楼房数量就是这 ...
- (转)Unity3d使用心得(1):ModelImporter的使用、在代码中添加动画片段。
在使用 Unity3d 倒入Fbx模型的时候,动画的动画片段需要自己手动添加模型多了以后会是一个不小的工作量. Unity3d支持 编辑器脚本来控制资源导入的过程.添加一个 AssetPostproc ...
- 程设刷题 | 程序设计实践II-2017(部分)
目录 1165-算术题 题目描述 代码实现 1184-Tourist 1 题目描述 代码实现 1186-Tourist 2 题目描述 代码实现 1224-LOVE 题目描述 代码实现 1256-湘潭大 ...
- ubuntu10.10编译TQ2440的x86-qtopia-2.2.0编译问题解决精简版
转:http://blog.csdn.net/zyxlinux888/article/details/6705480 操作:1.要安装系统缺失的类库和安装包(有些是非必须的):zyx@zyx:/$ s ...