蓝牙常用类及常用方法
BluetoothAdapter 蓝牙适配器
- cancelDiscovery() 取消发现,也就是说当我们正在搜索设备的时候调用这个方法将不再继续搜索,在销毁蓝牙广播前或与服务器连接前使用。
- disable() 关闭蓝牙
- enable() 打开蓝牙,这个方法打开蓝牙不会弹出提示,更多的时候我们需要问下用户是否打开,一下这两行代码同样是打开蓝牙,不过会提示用户:
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, requestCode); - getAddress() 获取本地蓝牙地址
- getDefaultAdapter() 获取默认BluetoothAdapter,唯一一个。
- getName() 获取本地蓝牙名称
- getRemoteDevice(String address) 根据蓝牙地址获取远程蓝牙设备
- getState() 获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)
- isDiscovering() 判断当前是否正在查找设备,是返回true
- isEnabled() 判断蓝牙是否打开,已打开返回true,否则,返回false
- listenUsingRfcommWithServiceRecord(String name,UUID uuid) 根据名称,UUID创建并返回BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步
- startDiscovery()开始搜索,startDiscovery()方法是一个异步方法,调用后会立即返回。该方法会进行对其他蓝牙设备的搜索,该过程会持续12秒。该方法调用后,搜索过程实际上是在一个System Service中进行的,所以可以调用cancelDiscovery()方法来停止搜索(该方法可以在未执行discovery请求时调用)。
请求Discovery后,系统开始搜索蓝牙设备,在这个过程中,系统会发送以下三个广播:
ACTION_DISCOVERY_START:开始搜索
ACTION_DISCOVERY_FINISHED:搜索结束
ACTION_FOUND:找到设备,这个Intent中包含两个extra fields:EXTRA_DEVICE和EXTRA_CLASS,分别包含BluetooDevice和BluetoothClass。
BluetoothDevice 描述了一个蓝牙设备
- createRfcommSocketToServiceRecord(UUIDuuid) 根据UUID创建并返回一个BluetoothSocket
- getState() 蓝牙状态这里要说一下,只有在 BluetoothAdapter.STATE_ON 状态下才可以监听,具体可以看andrid api;
- 这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter
BluetoothServerSocket
这个类一种只有三个方法两个重载的accept(),accept(inttimeout)两者的区别在于后面的方法指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行!还有一点需要注意的是,这两个方法都返回一个BluetoothSocket,最后的连接也是服务器端与客户端的两个BluetoothSocket的连接。
BluetoothSocket
跟BluetoothServerSocket相对,是客户端一共5个方法,不出意外,都会用到。
- close(), 关闭
- connect() 连接
- getInptuStream() 获取输入流
- getOutputStream() 获取输出流
- getRemoteDevice() 获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备
BluetoothClass 描述了设备通用特性和功能的蓝牙类
- class BluetoothClass.Device 定义所有设备类的常量
- public int getDeviceClass () 返回BluetoothClass中的设备类部分(主要的和较小的)
- public int getMajorDeviceClass () 返回BluetoothClass中设备类的主要部分
- class BluetoothClass.Service 定义所有服务类的常量
蓝牙配对与取消配对
- 蓝牙配对:
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
//版本号大于23,可以直接用device.createBond(),小于23时则用反射
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
device.createBond();
adapter.notifyDataSetChanged();
} else {//适配
try {
Method method = BluetoothDevice.class.getMethod("createBond");
method.invoke(device);
adapter.notifyDataSetChanged();
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
- 取消配对:
//取消配对
//好像只有在信号范围内才有效 (像系统一样直接可以取消没找到方法???????)
Method method = BluetoothDevice.class.getMethod("removeBond", (Class[]) null);
method.invoke(device, (Object[]) null);
adapter.notifyDataSetChanged();
popupWindow.dismiss();
popupWindow = null;
Toast.makeText(MainActivity.this, "取消配对成功!", Toast.LENGTH_SHORT).show();
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
Log.e("onClick", e + "");
}
}
蓝牙信号强度
- 获取蓝牙信号强度,在广播onReceive方法中通过intent获取
@Override
public void onReceive(Context context, Intent intent) {
int rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);//获取信号强度
}
- 蓝牙强度Rssi的取值范围
Rssi和接收功率有关,单位是dBm,一般为负值,反应的是信号的衰减程度,理想状态下(无衰减),Rssi = 0dBm,实际情况是,即使蓝牙设备挨得非常近,Rssi也只有-50dBm的强度,在传输过程中,不可避免要损耗。- 一般情况下,经典蓝牙强度
- -50 ~ 0dBm 信号强
- -70 ~-50dBm 信号中
- <-70dBm 信号弱
- 低功耗蓝牙分四级
- -60 ~ 0 4
- -70 ~ -60 3
- -80 ~ -70 2
- <-80 1
- 一般情况下,经典蓝牙强度
- rssi信号强度与距离的转换公式
- d = 10^((abs(rssi) - A) / (10 * n))
- 其中:d - 计算所得距离(单位:m),rssi - 接收信号强度,A - 发射端和接收端相隔1米时的信号强度,n - 环境衰减因子。
基本用法
- 获取本地蓝牙适配器
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- 打开蓝牙
if (bluetoothAdapter == null) {
Toast.makeText(this, "本设备没有蓝牙模块", Toast.LENGTH_SHORT).show();
finish();
} else if (!bluetoothAdapter.isEnabled()) {
//直接开启蓝牙
// bluetoothAdapter.enable();
//请求开启蓝牙
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, 0);
}
- 搜索设备
private void startBluetooth() {
if (adapter == null) {
return;
}
if (bluetoothAdapter == null) {
//创建蓝牙是配置
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
bluetoothAdapter.startDiscovery();
//创建广播接收蓝牙消息
receiver = new BluetoothReceiver(this.adapter, bluetoothAdapter, this);
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
//注册蓝牙广播
this.registerReceiver(receiver, filter);
}
定义BroadcastReceiver:
if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
adapter.add(device);
adapter.notifyDataSetChanged();
} else if (intent.getAction().equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
context.unregisterReceiver(this);
}
}
- 建立连接
- 服务器端:
BluetoothServerSocket serverSocket = mAdapter. listenUsingRfcommWithServiceRecord(serverSocketName,UUID);
serverSocket.accept();
- 客户端:
final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";
UUID uuid = UUID.fromString(SPP_UUID);
BluetoothSocket socket;
socket = device.createInsecureRfcommSocketToServiceRecord(uuid);
adapter.cancelDiscovery();
socket.connect();
- 数据传递
- 获取数据
//获取信息
@Override
public void run() {
try {
DataInputStream is = new DataInputStream(socket.getInputStream());
Log.e("run", "" + is.readUTF());
String msg;
while ((msg = is.readUTF()) != null) {
Message message = Message.obtain();
message.what = 0;
message.obj = "收到的消息-" + device.getName() + ":" + msg;
if (mListener != null) {
mListener.getMase(message);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
- 写入数据
public void send(final String msg) {
new Thread() {
@Override
public void run() {
try {
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
os.writeUTF(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
数据转换
使用socket.getInputStream接收到的数据是字节流,这样的数据是没法分析的,所以很多情况需要一个byte转十六进制String的函数:
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}