一、注意事项
1:android6.0使用蓝牙时,需要开启gps定位权限,不然无法搜索其它蓝牙设备。
二、权限
1:权限配置
<!--允许程序连接到已配对的蓝牙设备-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 允许程序发现和配对蓝牙设备 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!--android 6.0 涉及到的权限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 在SDCard中创建与删除文件的权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据的权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2:动态权限代码
由于需要用到存储卡,定位等,android6.0以上需要代码动态设置。
public static final int REQUEST_EXTERNAL_STORAGE = 1001;
public static String[] PERMISSIONS_STORAGE = {
Manifest.permission.BLUETOOTH,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
};
/**
* 首先判断当前系统是否是Android6.0(对应API 23)以及以上
* ActivityCompat.requestPermission方法的
* 第一个参数是目标Activity,填写this即可,
* 第二个参数是String[]字符数组类型的权限集,
* 第三个即请求码:
* @param activity
*/
public static void verifyStoragePermissions(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//检查权限是否被授权 被授权返回值为PERMISSION_GRANTED,否则返回PERMISSION_DENIED
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
//申请权限
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
}
/**
* * 判断权限集合
* * permissions 权限数组
* * return true-表示没有开权限 false-表示权限已开启
*/
public static boolean lacksPermissions(Context mContexts, String[] permissions) {
for (String permission : permissions) {
if (lacksPermission(mContexts,permission)) {
return true;
}
}
return false;
}
/**
* 判断是否缺少权限
*/
private static boolean lacksPermission(Context mContexts, String permission) {
return ContextCompat.checkSelfPermission(mContexts, permission) ==
PackageManager.PERMISSION_DENIED;
}
//这块是在调动搜索方法的时候判断权限
if (lacksPermissions(MainActivity.this, PermisionUtils.PERMISSIONS_STORAGE)) {
//申请权限
ActivityCompat.requestPermissions(MainActivity.this, PermisionUtils.PERMISSIONS_STORAGE, PermisionUtils.REQUEST_EXTERNAL_STORAGE);
} else {
if (null != bluemanager) {
//判断蓝牙是否开启
if (!bluemanager.getBlueToothStatus()) {
bluemanager.requestEnableBt();
} else {
//搜索方法
bluemanager.searchDevices();
}
}
}
/**
* 申请权限后回调onRequestPermissionResult函数,
* 第一个参数为请求码,
* 第二个参数是刚刚请求的权限集,
* 第三个参数是请求结果,0表示授权成功,-1表示授权失败:
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PermisionUtils.REQUEST_EXTERNAL_STORAGE) {
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == 0) {
//授权成功!
} else if (grantResults[i] == -1) {
// verifyStoragePermissions(this);
Toast.makeText(MainActivity.this, "您拒绝了程序需要的权限,无法使用App!!!", Toast.LENGTH_SHORT).show();
}
}
}
}
三、开启蓝牙
// 检查设备是否支持蓝牙
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null)
{
// 设备不支持蓝牙
}
/**
* 当前蓝牙状态
*
* @return true 打开,false 关闭
*/
public boolean getBlueToothStatus() {
assert (mBluetoothAdapter != null);
return mBluetoothAdapter.isEnabled();
}
/**
* 开启蓝牙
* mBluetoothAdapter.disable(); // 关闭
*/
public void requestEnableBt() {
if (!mBluetoothAdapter.isEnabled())
//开启蓝牙
mBluetoothAdapter.enable();
}
四、搜索周围的蓝牙设备
适配器搜索蓝牙设备后将结果以广播形式传出去,所以需要自定义一个继承广播的类,在onReceive方法中获得并处理蓝牙设备的搜索结果。
/**
* 注册广播
*/
public void registerBTReceiver() {
// 设置广播信息过滤
IntentFilter intentFilter = new IntentFilter();
// 注册接收查找到设备action接收器
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
// 注册开始搜索action接收器
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
// 注册查找结束action接收器
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
// 手机蓝牙开启关闭时发送 监听手机本身蓝牙状态的广播
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
// 指明一个远程设备的连接状态的 蓝牙设备配对和解除配对时发送
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
// 注册广播接收器,接收并处理搜索结果
mContext.registerReceiver(BTReceive, intentFilter);
}
/**
* 广播接收者
*/
private BroadcastReceiver BTReceive = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case BluetoothAdapter.ACTION_STATE_CHANGED:
int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
switch (blueState) {
case BluetoothAdapter.STATE_TURNING_ON:
Log.i(TAG, "onReceive: 蓝牙打开中");
break;
case BluetoothAdapter.STATE_ON:
Log.i(TAG, "onReceive: 蓝牙已打开");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log.i(TAG, "onReceive: 蓝牙关闭中");
break;
case BluetoothAdapter.STATE_OFF:
Log.i(TAG, "onReceive已关闭");
break;
}
break;
case BluetoothDevice.ACTION_FOUND:
// 获取查找到的蓝牙设备
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 如果查找到的设备符合要连接的设备,处理
if (null != device.getName()) {
if (device.getName().equalsIgnoreCase("HC-06")) {
//获取距离
int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
DevicesModel devicesModel = new DevicesModel();
devicesModel.setAddress(device.getAddress());
devicesModel.setName(device.getName());
devicesModel.setRssi(rssi);
mBondedList.add(devicesModel);
}
}
break;
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
//蓝牙设备配对和解除配对时发送
/* // 获取蓝牙设备的连接状态
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String name = device.getName();
Log.d("aaa", "device name: " + name);
int connectState = device.getBondState();
// 已配对
switch (connectState) {
case BluetoothDevice.BOND_NONE:
break;
case BluetoothDevice.BOND_BONDING:
Log.i(TAG, "onReceive: 正在配对");
DevicesModel devicesModel = new DevicesModel();
devicesModel.setName(device.getName());
devicesModel.setAddress(device.getAddress());
mNewList.add(devicesModel);
break;
case BluetoothDevice.BOND_BONDED:
//配对成功可以直接连接
Log.i(TAG, "onReceive: 配对成功");
DevicesModel model = new DevicesModel();
model.setName(device.getName());
model.setAddress(device.getAddress());
mBondedList.add(model);
break;
default:
break;
}*/
break;
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
if (mOnSearchDeviceListener != null)
mOnSearchDeviceListener.onStartDiscovery();
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
//搜索完成 选择要连接的设备
mOnSearchDeviceListener.onSearchCompleted(mBondedList);
break;
}
}
};
五、蓝牙连接
/**
* 连接蓝牙服务的线程
*/
private class ConnectDeviceRunnable implements Runnable {
private String mac;
public ConnectDeviceRunnable(String mac) {
this.mac = mac;
}
@Override
public void run() {
try {
if (onConnectListener == null) {
Log.i(TAG, "the connectListener is null !");
return;
}
// 准备连接设备,关闭服务查找
mBluetoothAdapter.cancelDiscovery();
// 得到蓝牙设备句柄
BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mac);
Log.d(TAG, "准备连接" + remoteDevice.getAddress() + " " + remoteDevice.getName());
// 用服务号得到socket
try {
mSocket = remoteDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(Constants.STR_UUID));
} catch (IOException e) {
ToestUtil.showToast(mContext, "--连接失败!");
}
//正在连接
onConnectListener.onConnectting();
try {
mSocket.connect();
onConnectListener.onConnectSuccess(remoteDevice.getName());
} catch (IOException e) {
Log.i(TAG, "连接失败!!!!" + e.toString());
return;
}
//打开接收线程
try {
mInputStream = mSocket.getInputStream(); //得到蓝牙数据输入流
} catch (IOException e) {
Log.i(TAG, "接收数据失败!!!" + e.toString());
return;
}
try {
mOutputStream = mSocket.getOutputStream(); //得到蓝牙数据输出流
} catch (IOException e) {
Log.i(TAG, "读取数据失败!!!" + e.toString());
return;
}
} catch (Exception e) {
e.printStackTrace();
//连接失败
onConnectListener.onConnectFailed();
}
}
}
六、读取设备消息
/**
* 读取bluetooth流线程
*/
private class ReadRunnable implements Runnable {
@Override
public void run() {
try {
if (mSocket == null) {
Log.i(TAG, "run: 请先连接蓝牙设备");
return;
}
BufferedReader in = new BufferedReader(new InputStreamReader(
mInputStream));
String line = "";
StringBuffer oneRecord = new StringBuffer();
while ((line = in.readLine()) != null) {
Log.i(TAG, "读取到的每一行 " + line);
if (handler != null) {
Message message = handler.obtainMessage();
message.what = 1;
message.obj = line;
handler.sendMessage(message);
}
}
} catch (Exception e) {
e.printStackTrace();
onReceiveMessageListener.onConnectionLost(e);
}
}
}
//处理ui
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String message = msg.obj.toString();
switch (msg.what) {
case 1:
Log.i(TAG, "读取到的每一行 " + message);
onReceiveMessageListener.readData(message);
break;
}
}
};
七、写数据
class WriteRunnable implements Runnable {
private String sendContent;
public WriteRunnable(String sendcontent) {
Log.i(TAG, "WriteRunnable: ");
this.sendContent = sendcontent;
}
@Override
public synchronized void run() {
if (mOutputStream != null) {
Log.i(TAG, "run: mOutputStream != null");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(mOutputStream));
try {
writer.write(sendContent);
writer.newLine();
writer.flush();
Log.d(TAG, "send string message: " + sendContent);
onSendMessageListener.onSuccess(Constants.STATUS_OK, "send string message is success callback !");
} catch (IOException e) {
Log.i(TAG, "run: 发送异常");
onSendMessageListener.onConnectionLost(e);
}
} else {
Log.i(TAG, "WriteRunnable:mOutputStream == null ");
if (mSocket == null) {
Log.i(TAG, "run: 请先连接蓝牙设备");
return;
}
try {
mOutputStream = mSocket.getOutputStream(); //蓝牙连接输出流
} catch (IOException e) {
Log.i(TAG, "输入流run: ");
}
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(mOutputStream));
try {
writer.write(sendContent);
writer.newLine();
writer.flush();
Log.d(TAG, "send string message: " + sendContent);
onSendMessageListener.onSuccess(Constants.STATUS_OK, "send string message is success callback !");
} catch (IOException e) {
Log.i(TAG, "run: 发送异常");
}
}
}
}