Android BLE与终端通信(二)——Android Bluetooth基础搜索蓝牙设备显示列表
摘要
第一篇算是个热身,这一片开始来写些硬菜了,这篇就是实际和蓝牙打交道了,所以要用到真机调试哟,这篇我会把基本上要讲的概念都通俗易懂的来一遍,这样我们脑子里先有个逻辑,我们就好操作了,先看一下我们的剖析图
下面概念相关的可去原文查看:http://www.epx.com.br/artigos/bluetooth_gatt.php
一.蓝牙简介
蓝牙这个名称来自于第十世纪的一位丹麦国王哈拉尔蓝牙王,Blatand 在英文里的意思就是哈拉尔蓝牙王
可以被解释为 Bluetooth( 蓝牙 )因为国王喜欢吃蓝莓,牙龈每天都是蓝色的所以叫蓝牙。
在行业协会筹备阶段,需要一个极具有表现力的名字来命名这项高新技术。行业组织人员, 在经过一夜关于欧洲历史和未来无线技术发展的讨论后,有些人认为用 Blatand 国王的名字 命名再合适不过了。Blatand 国王将挪威,瑞典和丹麦统一起来;他的口齿伶俐,善于交际,就如同这项即将面世的技术,技术将被定义为允许不同工业领域之间的协调工作,保持着各个系统领域之间的良好交流,例如计算机,手机和汽车行业之间的工作。名字于是就这么定 下来了。
蓝牙的创始人是爱立信公司,爱立信早在 1994 年就已进行研发。1997 年,爱立信与 其他设备生产商联系,并激发了他们对该项技术的浓厚兴趣。 1998 年 2 月,跨国大公司, 包括诺基亚、苹果、三星组成的一个特殊兴趣小组(SIG),他们共同的目标是建立一 个全球性的小范围无线通信技术,即蓝牙。
而蓝牙这个标志的设计:它取自 Harald Bluetooth 名字中的「H」和「B」两个字母, 用古北欧字母来表示,将这两者结合起来,就成为了蓝牙的 logo
二.蓝牙的工作原理
1.蓝牙通信的主从关系
蓝牙技术规定每一对设备之间进行蓝牙通讯时,必须一个为主角色,另一为从角色,
才能进行通信,通信时,必须由主端进行查找,发起配对,建链成功后,双方即可收发数据。 理论上,一个蓝牙主端设备,可同时与 7 个蓝牙从端设备进行通讯。一个具备蓝牙通讯功 能的设备, 可以在两个角色间切换,平时工作在从模式,等待其它主设备来连接,需要时, 转换为主模式,向其它设备发起呼叫。一个蓝牙设备以主模式发起呼叫时,需要知道对方的 蓝牙地址,配对密码等信息,配对完成后,可直接发起呼叫。
2.蓝牙的呼叫过程
蓝牙主端设备发起呼叫,首先是查找,找出周围处于可被查找的蓝牙设备。主端设备 找到从端蓝牙设备后,与从端蓝牙设备进行配对,此时需要输入从端设备的 PIN 码,也有 设备不需要输入 PIN 码。配对完成后,从端蓝牙设备会记录主端设备的信任信息,此时主 端即可向从端设备发起呼叫,已配对的设备在下次呼叫时,不再需要重新配对。已配对的设备,做为从端的蓝牙耳机也可以发起建链请求,但做数据通讯的蓝牙模块一般不发起呼叫。链路建立成功后,主从两端之间即可进行双向的数据或语音通讯。在通信状态下,主端和从 端设备都可以发起断链,断开蓝牙链路。
3.蓝牙一对一的串口数据传输应用
蓝牙数据传输应用中,一对一串口数据通讯是最常见的应用之一,蓝牙设备在出厂前 即提前设好两个蓝牙设备之间的配对信息,主端预存有从端设备的 PIN 码、地址等,两端 设备加电即自动建链,透明串口传输,无需外围电路干预。一对一应用中从端设备可以设为两种类型,一是静默状态,即只能与指定的主端通信,不被别的蓝牙设备查找;二是开发状 态,既可被指定主端查找,也可以被别的蓝牙设备查找建链。
三.蓝牙 Android 编程应用
1.相关部署
Android BLE与终端通信(一)——Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址
下面依然会被提及
2.UUID
1.1 认识一下 UUID UUID 含义是通用唯一识别码 (Universally Unique Identifier),这 是一个软件建构的标 准,也是被开源软件基金会 (Open Software Foundation, OSF) 的组织应用在分布式计算 环境 (Distributed Computing Environment, DCE) 领域的一部分。
在蓝牙 3.0 及一下版本中,UUID 被用于唯一标识一个服务,比如文件传输服务,串口 服务、打印机服务等,如下:蓝牙串口服务
SerialPortServiceClass_UUID = ‘{00001101-0000-1000-8000-00805F9B34FB}’
LANAccessUsingPPPServiceClass_UUID = ‘{00001102-0000-1000-8000-00805F9B34FB}’拨号网络服务
DialupNetworkingServiceClass_UUID = ‘{00001103-0000-1000-8000-00805F9B34FB}’信息同步服务
IrMCSyncServiceClass_UUID = ‘{00001104-0000-1000-8000-00805F9B34FB}’ SDP_OBEXObjectPushServiceClass_UUID = ‘{00001105-0000-1000-8000-00805F9B34FB} ’文件传输服务
OBEXFileTransferServiceClass_UUID = ‘{00001106-0000-1000-8000-00805F9B34FB}’ IrMCSyncCommandServiceClass_UUID = ‘{00001107-0000-1000-8000-00805F9B34FB}’蓝牙的连接有主从设备,提供服务的可以认为是从设备。主设备通过 UUID 访问从设备 提供具有相同 UUID 的服务,从而建立客服端—服务器(C/S)模式。
四.实际应用
我们新建一个Eclipse工程:BluetoothGet
1.蓝牙协议
我们直接看图(简单的概念)
2.蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
3.编写程序
activity_main.xml
布局很简单,就一个搜索按钮和一个textview的列表
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btnSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="搜索蓝牙设备" />
<TextView
android:id="@+id/tvDevices"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
步骤分析
1.首先获取本地的蓝牙适配器
// 获取本地蓝牙适配器
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
2.判断手机是否支持蓝牙
// 判断手机是否支持蓝牙
if (mBluetoothAdapter == null) {
Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
finish();
}
3.判断蓝牙是否打开
// 判断是否打开蓝牙
if (!mBluetoothAdapter.isEnabled()) {
// 弹出对话框提示用户是后打开
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, 1);
// 不做提示,强行打开
// mBluetoothAdapter.enable();
}
4.获取已经配对的设备
// 获取已经配对的设备
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// 判断是否有配对过的设备
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
// 遍历到列表中
tvDevices.append(device.getName() + ":" + device.getAddress());
Log.i("已配对设备", tvDevices.getText().toString());
}
}
5.搜索的点击事件
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSearch:
//设置进度条
setProgressBarIndeterminateVisibility(true);
setTitle("正在搜索...");
// 判断是否在搜索,如果在搜索,就取消搜索
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
// 开始搜索
mBluetoothAdapter.startDiscovery();
break;
}
}
6.搜索的广播
1.注册广播
/**
* 异步搜索蓝牙设备——广播接收
*/
// 找到设备的广播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
// 注册广播
registerReceiver(receiver, filter);
// 搜索完成的广播
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
// 注册广播
registerReceiver(receiver, filter);
1.广播接收器
// 广播接收器
private final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 收到的广播类型
String action = intent.getAction();
// 发现设备的广播
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 从intent中获取设备
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 判断是否配对过
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
// 添加到列表
tvDevices.append(device.getName() + ":"
+ device.getAddress() + "\n");
}
// 搜索完成
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
.equals(action)) {
//关闭进度条
setProgressBarIndeterminateVisibility(true);
setTitle("搜索完成!");
}
}
};
好了,到此,我们的程序算是完成了
MainActivity
package com.lgl.bluetoothget;
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.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener {
// 本地蓝牙适配器
private BluetoothAdapter mBluetoothAdapter;
// 搜索到蓝牙添加
private TextView tvDevices;
// 搜索蓝牙的按钮
private Button btnSearch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
tvDevices = (TextView) findViewById(R.id.tvDevices);
btnSearch = (Button) findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(this);
// 获取本地蓝牙适配器
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 判断手机是否支持蓝牙
if (mBluetoothAdapter == null) {
Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
finish();
}
// 判断是否打开蓝牙
if (!mBluetoothAdapter.isEnabled()) {
// 弹出对话框提示用户是后打开
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, 1);
// 不做提示,强行打开
// mBluetoothAdapter.enable();
}
// 获取已经配对的设备
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter
.getBondedDevices();
// 判断是否有配对过的设备
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
// 遍历到列表中
tvDevices.append(device.getName() + ":" + device.getAddress());
Log.i("已配对设备", tvDevices.getText().toString());
}
}
/**
* 异步搜索蓝牙设备——广播接收
*/
// 找到设备的广播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
// 注册广播
registerReceiver(receiver, filter);
// 搜索完成的广播
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
// 注册广播
registerReceiver(receiver, filter);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSearch:
// 设置进度条
setProgressBarIndeterminateVisibility(true);
setTitle("正在搜索...");
// 判断是否在搜索,如果在搜索,就取消搜索
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
// 开始搜索
mBluetoothAdapter.startDiscovery();
break;
}
}
// 广播接收器
private final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 收到的广播类型
String action = intent.getAction();
// 发现设备的广播
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 从intent中获取设备
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 判断是否配对过
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
// 添加到列表
tvDevices.append(device.getName() + ":"
+ device.getAddress() + "\n");
}
// 搜索完成
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
.equals(action)) {
// 关闭进度条
setProgressBarIndeterminateVisibility(true);
setTitle("搜索完成!");
}
}
};
}
我们运行一下