蓝牙的最基本的用法,搜索和配对!
首先看下效果如下
开启蓝牙需要注册清单文件,初始化蓝牙适配器,添加广播监听,最后才是正式开启蓝牙。
5.0以上的版本需要加入gps权限
6.0以上的版本需要手动申请定位权限
注:6.0加入权限之后记得手动获取权限!不然蓝牙功能无法正常使用
初始化相当简单直接调用getDefaultAdapter
方法就可以了
//初始化蓝牙
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
Toast.makeText(this, "设备不存在蓝牙", Toast.LENGTH_SHORT).show();
return;
}
初始化之后就可以用BluetoothAdapter对象进行对蓝牙的操作
BluetoothAdapter常用方法表格如下
方法 | 说明 |
---|---|
getDefaultAdapter | 默认本地适配器,如果此硬件平台不支持蓝牙,则为空。 |
isEnabled | 如果蓝牙当前已启用并准备好使用,则返回true。 |
getState | 获取本地蓝牙适配器的当前状态。STATE_OFF(关闭)、STATE_TURNING_ON(正在打开)、STATE_ON(开启)、STATE_TURNING_OFF(正在关闭) |
enable | 强制开启蓝牙 |
disable | 强制关闭蓝牙 |
disable(boolean persist) | 强制关闭蓝牙,是否保留设置。 |
getAddress | 获取蓝牙地址 |
getName | 获取蓝牙名字 |
factoryReset | 出厂重置蓝牙 |
setName(String name) | 设置蓝牙名字 |
getScanMode | 获取蓝牙的扫描模式,扫描模式确定是否可以连接或被发现。SCAN_MODE_NONE(蓝牙未开启)、SCAN_MODE_CONNECTABLE(不可见)、SCAN_MODE_CONNECTABLE_DISCOVERABLE(可见) |
setScanMode(@ScanMode int mode, int duration) | (需要使用反射)设置蓝牙的扫描模式,单独的setScanMode(int mode)改变状态设置默认蓝牙可见时间 |
getDiscoverableTimeout、setDiscoverableTimeout(int timeout) | (需要使用反射)获取和设置默认蓝牙可见时间 |
startDiscovery | 开始扫描其他蓝牙 |
cancelDiscovery | 取消扫描 |
isDiscovering | 是否在扫描中 |
getBondedDevices | 获取已绑定蓝牙 |
getConnectionState | 获取蓝牙连接状态。STATE_CONNECTED(已连接)、STATE_DISCONNECTED(未连接)、STATE_CONNECTING(连接中) |
初始化之后就需要监听蓝牙的广播,不然无法获取搜索到了蓝牙设备
//绑定广播
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);//找到设备的广播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索完成的广播
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//蓝牙状态改变的广播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//开始扫描的广播
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//配对状态改变
registerReceiver(mReceiver, filter);
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Log.e("-s-", "action = " + action);
switch (action) {
//蓝牙状态改变
case BluetoothAdapter.ACTION_STATE_CHANGED:
Log.e("-s-", "蓝牙状态改变");
checkBluetooth();
break;
//开始扫描
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
Log.e("-s-", "开始扫描蓝牙");
allAdapter.clear();
changeText("开始扫描蓝牙");
showToast("开始扫描蓝牙");
break;
//搜索完成
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
Log.e("-s-", "搜索完成");
changeText("搜索完成");
showToast("搜索完成");
break;
//找到设备
case BluetoothDevice.ACTION_FOUND:
Log.e("-s-", "找到设备");
changeText("搜索中...");
printDevice(bluetoothDevice);
allAdapter.add(bluetoothDevice);
break;
//配对状态改变
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
Log.e("-s-", "配对状态改变");
if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDING) {
changeText(bluetoothDevice.getName() + " 配对中");
Log.e("-s-", "配对中");
} else if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
changeText(bluetoothDevice.getName() + " 已配对成功");
Log.e("-s-", "已配对成功");
} else {
changeText(bluetoothDevice.getName() + " 已取消配对");
Log.e("-s-", "已取消配对");
}
printDevice(bluetoothDevice);
updataBonds();
break;
default:
}
}
};
以上代码有常用的几个蓝牙相关的广播,这边主要讲下另一个比较需要注意的APIBluetoothDevice
以下表格是常用的BluetoothDevice方法
方法 | 说明 |
---|---|
getAddress | 获取蓝牙设备的地址 |
getName | 获取蓝牙设备的名称(可能会空) |
getType | 获得蓝牙的设备类型。DEVICE_TYPE_UNKNOWN(未知)、DEVICE_TYPE_CLASSIC(br/edr设备)、DEVICE_TYPE_LE(低能量-仅限LE)、DEVICE_TYPE_DUAL(双模,BR/EDR/LE) |
getAlias | (需要反射调用)获取蓝牙设备的别名(可能会空) |
getAliasName | (需要反射调用)获取蓝牙的名称,优先别名,别名为空则返回名称,名称也有可能为空 |
createBond | 启动与远程设备的连接(配对)过程。 |
createBond(int transport) | (需要反射调用)使用指定的传输启动与远程设备的绑定(配对)过程。TRANSPORT_AUTO(没有偏好的GATT连接)、TRANSPORT_BREDR(优先和BR/EDR设备GATT连接)、TRANSPORT_LE(优先和LE设备GATT连接) |
cancelBondProcess | (需要反射调用)取消createBond创建的配对请求 |
removeBond | (需要反射调用)取消设备配对 |
getBondState | 获取设备的绑定状态。BOND_NONE(未绑定)、BOND_BONDING(绑定中)、BOND_BONDED(已绑定) |
isConnected | 返回是否有与此设备的打开连接。 |
getBluetoothClass | 获取远程设备的蓝牙类。 |
有了BluetoothAdapter的初始化对象和广播监听蓝牙之后就可以开启蓝牙了!
上面BluetoothAdapter的常用方法里面有一个强制开启蓝牙的方法
//强制开启蓝牙
bluetoothAdapter.enable();
在低于5.0以下的手机会直接打开蓝牙,以上就会以弹窗的形式提示是否允许。
if (!bluetoothAdapter.isEnabled()) {
//单独打开蓝牙 可以连接 默认不可见
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE);
}
首先判断了蓝牙是否可用并且没有打开,在正常打开蓝牙。这样打开的蓝牙默认是可连接但不会被其他设备发现。
if (!bluetoothAdapter.isEnabled()) {
//打开蓝牙并且自己蓝牙可见
Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);//默认是120秒
discoveryIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);//设置持续时间(最多300秒)
startActivityForResult(discoveryIntent, REQUEST_ENABLE);
}
还是一样首先判断了蓝牙是否可用并且没有打开,在正常打开蓝牙。这样打开的蓝牙可以连接并且可以被其他设备发现。
上面BluetoothAdapter的常用方法里面有一个强制关闭蓝牙的方法,正常我们直接使用这个方法就可以了,因为没有其他的方法了。
if (bluetoothAdapter.isEnabled()) {
bluetoothAdapter.disable();
}
//是否可用并开启蓝牙
if (bluetoothAdapter.isEnabled()) {
//是否在扫描状态
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
//开始扫描
bluetoothAdapter.startDiscovery();
}
调用搜索之后 结果都是在广播中回调的
搜索广播片段如下 添加广播中有完整的片段
String action = intent.getAction();
//获取获得的设备
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (action) {
//开始扫描
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
Log.e("-s-", "开始扫描蓝牙");
...
break;
//搜索完成
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
Log.e("-s-", "搜索完成");
...
break;
//找到设备
case BluetoothDevice.ACTION_FOUND:
Log.e("-s-", "找到设备");
//每次会获取到一个设备,可以加入到自己的listview中作为展示
...
break;
}
直接取上面的方法getBondedDevices
就可以了。
//清空已配对listview的适配器
bondAdapter.clear();
//遍历已配对的Set
for (BluetoothDevice device : bluetoothAdapter.getBondedDevices()) {
//加入已配对listview的适配器
bondAdapter.add(device);
}
由于createBond
方法其实没有隐藏,所以可以直接调用方法进行配对,然后再广播中进行监听实现配对成功和失败的提示。
//正常配对
if (device.createBond()) {
Log.e("-s-", "开始配对");
...
} else {
Log.e("-s-", "配对失败");
...
}
或者调用反射来实现配对,网上找到的资料一般都是下面这个。但是上面的正常配对其实也是可以的。
//反射配对
try {
Method method = BluetoothDevice.class.getMethod("createBond");
boolean result = (boolean) method.invoke(device);
if (result) {
Log.e("-s-", "开始配对");
...
}else {
Log.e("-s-", "配对失败");
...
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.e("-s-", "配对失败");
...
}
由于没有取消方法被隐藏了,就必须用反射来实现取消了
//反射取消配对
try {
Method method = BluetoothDevice.class.getMethod("removeBond");
boolean result = (boolean) method.invoke(device);
if (result) {
Log.e("-s-", "取消成功");
....
} else {
Log.e("-s-", "取消失败");
...
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.e("-s-", "取消失败");
...
}
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.os.ParcelUuid;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import com.gjn.baserecycleradapterlibrary.BaseRecyclerAdapter;
import com.gjn.baserecycleradapterlibrary.RecyclerViewHolder;
import com.gjn.permissionlibrary.PermissionUtils;
import com.gjn.testproject.R;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BlueActivity extends AppCompatActivity {
private static final int REQUEST_ENABLE = 0x1001;
private static final int REQUEST_DISABLE = 0x1002;
Button btn1, btn2, btn3, btn4, btn5, btn6;
TextView tv;
RecyclerView rv, rv2;
Switch sw;
private BluetoothAdapter bluetoothAdapter;
private BaseRecyclerAdapter allAdapter;
private BaseRecyclerAdapter bondAdapter;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Log.e("-s-", "action = " + action);
switch (action) {
//蓝牙状态改变
case BluetoothAdapter.ACTION_STATE_CHANGED:
Log.e("-s-", "蓝牙状态改变");
checkBluetooth();
break;
//开始扫描
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
Log.e("-s-", "开始扫描蓝牙");
allAdapter.clear();
changeText("开始扫描蓝牙");
showToast("开始扫描蓝牙");
break;
//搜索完成
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
Log.e("-s-", "搜索完成");
changeText("搜索完成");
showToast("搜索完成");
break;
//找到设备
case BluetoothDevice.ACTION_FOUND:
Log.e("-s-", "找到设备");
changeText("搜索中...");
printDevice(bluetoothDevice);
allAdapter.add(bluetoothDevice);
break;
//配对状态改变
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
Log.e("-s-", "配对状态改变");
if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDING) {
changeText(bluetoothDevice.getName() + " 配对中");
Log.e("-s-", "配对中");
} else if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
changeText(bluetoothDevice.getName() + " 已配对成功");
Log.e("-s-", "已配对成功");
} else {
changeText(bluetoothDevice.getName() + " 已取消配对");
Log.e("-s-", "已取消配对");
}
printDevice(bluetoothDevice);
updataBonds();
break;
default:
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blue);
PermissionUtils.requestPermissions(this, PermissionUtils.CODE_LOCATION, PermissionUtils.CODE_PHONE);
btn1 = findViewById(R.id.btn_1);
btn2 = findViewById(R.id.btn_2);
btn3 = findViewById(R.id.btn_3);
btn4 = findViewById(R.id.btn_4);
btn5 = findViewById(R.id.btn_5);
btn6 = findViewById(R.id.btn_6);
tv = findViewById(R.id.tv_state);
sw = findViewById(R.id.sw_state);
rv = findViewById(R.id.rv_ly);
rv2 = findViewById(R.id.rv_ypd);
//初始化蓝牙
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
Toast.makeText(this, "设备不存在蓝牙", Toast.LENGTH_SHORT).show();
return;
}
//绑定广播
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);//找到设备的广播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索完成的广播
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//蓝牙状态改变的广播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//开始扫描的广播
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//配对状态改变
registerReceiver(mReceiver, filter);
//初始化RecyclerView和适配器
rv.setLayoutManager(new LinearLayoutManager(this));
rv2.setLayoutManager(new LinearLayoutManager(this));
allAdapter = new BaseRecyclerAdapter(this, R.layout.item_blue, null) {
@Override
public void bindData(RecyclerViewHolder holder, BluetoothDevice device, int position) {
holder.setTextViewText(R.id.tv_name, device.getName());
holder.setTextViewText(R.id.tv_address, device.getAddress());
if (BluetoothDevice.BOND_BONDED == device.getBondState()) {
holder.setTextViewText(R.id.tv_state, "已配对");
} else {
holder.setTextViewText(R.id.tv_state, "未配对");
}
}
};
rv.setAdapter(allAdapter);
bondAdapter = new BaseRecyclerAdapter(this, R.layout.item_blue, null) {
@Override
public void bindData(RecyclerViewHolder holder, BluetoothDevice device, int position) {
holder.setTextViewText(R.id.tv_name, device.getName());
holder.setTextViewText(R.id.tv_address, device.getAddress());
if (BluetoothDevice.BOND_BONDED == device.getBondState()) {
holder.setTextViewText(R.id.tv_state, "已配对");
} else {
holder.setTextViewText(R.id.tv_state, "未配对");
}
}
};
rv2.setAdapter(bondAdapter);
checkBluetooth();
initData();
}
private void initData() {
allAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int pos) {
bond(allAdapter.getItem(pos));
}
});
bondAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int pos) {
unBond(bondAdapter.getItem(pos));
}
});
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!bluetoothAdapter.isEnabled()) {
//单独打开蓝牙
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE);
}
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bluetoothAdapter.enable();
}
});
btn5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Method setScanMode = BluetoothAdapter.class.getMethod("setScanMode", int.class);
boolean result = (boolean) setScanMode.invoke(bluetoothAdapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
Log.e("-s-", "result = " + result);
Method getDiscoverableTimeout = BluetoothAdapter.class.getMethod("getDiscoverableTimeout");
int time = (int) getDiscoverableTimeout.invoke(bluetoothAdapter);
Log.e("-s-", "time = " + time);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
btn6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//打开蓝牙并且自己蓝牙可见
Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);//默认是120秒
discoveryIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);//设置持续时间(最多300秒)
startActivityForResult(discoveryIntent, REQUEST_ENABLE);
}
});
btn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bluetoothAdapter.isEnabled()) {
bluetoothAdapter.disable();
}
}
});
btn4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bluetoothAdapter.isEnabled()) {
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
bluetoothAdapter.startDiscovery();
}
}
});
}
private void unBond(BluetoothDevice device) {
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
//反射取消配对
try {
Method method = BluetoothDevice.class.getMethod("removeBond");
boolean result = (boolean) method.invoke(device);
if (result) {
Log.e("-s-", "取消成功");
showToast("取消成功");
} else {
Log.e("-s-", "取消失败");
showToast("取消失败");
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
private void bond(BluetoothDevice device) {
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
// //反射配对
// try {
// Method method = BluetoothDevice.class.getMethod("createBond");
// boolean result = (boolean) method.invoke(device);
// if (result) {
// Log.e("-s-", "配对成功");
// showToast("配对成功");
// }else {
// Log.e("-s-", "配对失败");
// showToast("配对失败");
// }
// } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
// e.printStackTrace();
// }
//正常配对
if (device.createBond()) {
Log.e("-s-", "开始配对");
tv.setText(device.getName() + " 开始配对");
showToast("开始配对");
} else {
Log.e("-s-", "配对失败");
showToast(device.getName() + " 配对失败");
}
}
}
private void changeText(String str) {
tv.setText(str);
}
private void showToast(String s) {
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
private void checkBluetooth() {
switch (bluetoothAdapter.getScanMode()) {
case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Log.e("-s-", "蓝牙可连接");
tv.setText("蓝牙可连接");
break;
case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Log.e("-s-", "蓝牙可连接可发现");
tv.setText("蓝牙可连接可发现");
break;
case BluetoothAdapter.SCAN_MODE_NONE:
default:
Log.e("-s-", "蓝牙不可连接不可见");
tv.setText("蓝牙不可连接不可见");
}
if (bluetoothAdapter.isEnabled()) {
sw.setChecked(true);
updataBonds();
} else {
sw.setChecked(false);
allAdapter.clear();
bondAdapter.clear();
}
}
private void updataBonds() {
bondAdapter.clear();
for (BluetoothDevice device : bluetoothAdapter.getBondedDevices()) {
bondAdapter.add(device);
}
}
private void printDevice(BluetoothDevice device) {
Log.e("-s-", "Name = " + device.getName());
Log.e("-s-", "Address = " + device.getAddress());
Log.e("-s-", "BondState = " + device.getBondState());
Log.e("-s-", "Type = " + device.getType());
if (device.getUuids() != null) {
for (ParcelUuid uuid : device.getUuids()) {
Log.e("-s-", "uuid = " + uuid);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
}
Android BLE 蓝牙开发入门
Android蓝牙开发(一)之打开蓝牙和设备搜索
Android蓝牙开发(二)之蓝牙配对和蓝牙连接
Android 低功耗蓝牙(BLE)开发(1)-- 基本概念
Android 低功耗蓝牙(BLE)开发(2)-- BluetoothAdapter详解
Android 低功耗蓝牙(BLE)开发(3)-- BluetoothDevice详解
Android 低功耗蓝牙(BLE)开发(4)-- 蓝牙扫描和连接