最近需要开发一个蓝牙连接传数据的功能,自己不太会,就查了一下,出来结果后,给自己做个记录吧
主要参考的文章:
https://blog.csdn.net/zqf_888/article/details/81060606
https://blog.csdn.net/yaojie5519/article/details/89056026
开发配置:
在AndroidManifest.xml中配置权限
<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- 这个权限用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<!-- 这个权限用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
主要流程:
开启蓝牙
检测蓝牙
配对蓝牙
连接
通信
开启蓝牙
//蓝牙相关
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null){
// 说明此设备不支持蓝牙操作
XToastUtils.info("此设备不支持蓝牙");
return ;
}else {
//蓝牙还未打开,打开蓝牙
if(!mBluetoothAdapter.isEnabled()){
//第一种方法(弹出对话框提示信息)
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivity(enabler);
// //第二种方法(直接打开,没有提示)
// BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// bluetoothAdapter.enable();
}
}
动态权限
/**
* targetSdkVersion若是大于或等于23,除了添加了蓝牙权限外,还要动态获取位置权限,才能将搜索 到的蓝牙设备显示出来。
* 若是小于,则不需要动态获取权限。
* 动态申请权限
*/
public void applyPermission() {
if (Build.VERSION.SDK_INT >= 23) {
//检查是否已经给了权限
int checkpermission = ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.ACCESS_FINE_LOCATION);
if (checkpermission != PackageManager.PERMISSION_GRANTED) {//没有给权限
Log.e("permission", "动态申请");
//参数分别是当前活动,权限字符串数组,requestcode
ActivityCompat.requestPermissions(ProDataUploadActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}else{
XToastUtils.info("已经有权限了");
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
XToastUtils.info("已授权");
} else {
XToastUtils.error("拒绝授权");
}
}
开启广播接收扫描结果 和 配对结果
//扫描
scanBlueCallBack = new ScanBlueCallBack();
scanBlueReceiver = new ScanBlueReceiver(scanBlueCallBack);
IntentFilter filter1 = new IntentFilter(android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED);
IntentFilter filter2 = new IntentFilter(android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
IntentFilter filter3 = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(scanBlueReceiver,filter1);
registerReceiver(scanBlueReceiver,filter2);
registerReceiver(scanBlueReceiver,filter3);
//配对
pinBlueCallBack = new PinBlueCallBack();
pinBlueReceiver = new PinBlueReceiver(pinBlueCallBack);
IntentFilter filter4 = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
IntentFilter filter5 = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(pinBlueReceiver,filter4);
registerReceiver(pinBlueReceiver,filter5);
package com.bonus.sdsagmeasureapp.ui.adapter.BlueBooth;
/**
* ************************************
*
* @ClassName:
* @Description: java类作用描述
* @Author: fl
* @CreateDate: 2021/6/10 15:43
* @Version: 1.0
* ************************************
*/
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
*扫描广播接收类
* Created by fl on 2021/6/13.
*/
public class ScanBlueReceiver extends BroadcastReceiver {
private static final String TAG = ScanBlueReceiver.class.getName();
private ScanBlueCallBack callBack;
public ScanBlueReceiver(ScanBlueCallBack callBack){
this.callBack = callBack;
}
//广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "action:" + action);
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device != null && "HC-06".equals(device.getName())){
Log.d(TAG, "设备名称:" + device.getName());
}else{
return;
}
switch (action){
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
Log.d(TAG, "开始扫描...");
callBack.onScanStarted();
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
Log.d(TAG, "结束扫描...");
callBack.onScanFinished();
break;
case BluetoothDevice.ACTION_FOUND:
Log.d(TAG, "发现设备...");
callBack.onScanning(device);
break;
}
}
}
package com.bonus.sdsagmeasureapp.ui.adapter.BlueBooth;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.bonus.sdsagmeasureapp.util.ByteUtil;
import java.lang.reflect.Method;
/**配对广播接收类
*
*/
public class PinBlueReceiver extends BroadcastReceiver {
private String pin = "1234"; //此处为你要连接的蓝牙设备的初始密钥,一般为1234或0000
private static final String TAG = PinBlueReceiver.class.getName();
private PinBlueCallBack callBack;
public PinBlueReceiver(PinBlueCallBack callBack){
this.callBack = callBack;
}
//广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "action:" + action);
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)){
try {
callBack.onBondRequest();
//1.确认配对
// ClsUtils.setPairingConfirmation(device.getClass(), device, true);
Method setPairingConfirmation = device.getClass().getDeclaredMethod("setPairingConfirmation",boolean.class);
setPairingConfirmation.invoke(device,true);
//2.终止有序广播
Log.d("order...", "isOrderedBroadcast:"+isOrderedBroadcast()+",isInitialStickyBroadcast:"+isInitialStickyBroadcast());
abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。
//3.调用setPin方法进行配对...
// boolean ret = ClsUtils.setPin(device.getClass(), device, pin);
Method removeBondMethod = device.getClass().getDeclaredMethod("setPin", new Class[]{byte[].class});
// Boolean returnValue = (Boolean) removeBondMethod.invoke(device, new Object[]{ByteUtil.hexStringToByte(pin)});
Boolean returnValue = (Boolean) removeBondMethod.invoke(device, new Object[]{pin.getBytes()});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
switch (device.getBondState()) {
case BluetoothDevice.BOND_NONE:
Log.d(TAG, "取消配对");
callBack.onBondFail(device);
break;
case BluetoothDevice.BOND_BONDING:
Log.d(TAG, "配对中");
callBack.onBonding(device);
break;
case BluetoothDevice.BOND_BONDED:
Log.d(TAG, "配对成功");
callBack.onBondSuccess(device);
break;
}
}
}
}
开启扫描 和结束扫描可以做一些你想要的操作
package com.bonus.sdsagmeasureapp.ui.adapter.BlueBooth;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.ScanCallback;
/**
* ************************************
*
* @ClassName: ScanBlueCallBack
* @Description: java类作用描述
* @Author: fl
* @CreateDate: 2021/6/10 18:11
* @Version: 1.0
* ************************************
*/
public class ScanBlueCallBack extends ScanCallback {
BluetoothDevice device;
public BluetoothDevice getDevice() {
return device;
}
public void setDevice(BluetoothDevice device) {
this.device = device;
}
/**
* 开始扫描
*/
public void onScanStarted() {
}
/**
* 结束扫描
*/
public void onScanFinished() {
}
/**
* 将扫描的设备存储起来后面连接使用
* @param device
*/
public void onScanning(BluetoothDevice device) {
this.device = device;
}
}
package com.bonus.sdsagmeasureapp.ui.adapter.BlueBooth;
import android.bluetooth.BluetoothDevice;
/**
* ************************************
* 配对成功
* @ClassName: PinBlueCallBack
* @Description: java类作用描述
* @Author: fl
* @CreateDate: 2021/6/10 21:22
* @Version: 1.0
* ************************************
*/
public class PinBlueCallBack {
private BluetoothDevice device;
/**
* 配对成功 标志
*/
public boolean flag = false;
public BluetoothDevice getDevice() {
return device;
}
public void setDevice(BluetoothDevice device) {
this.device = device;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public void onBondRequest() {
}
public void onBondFail(BluetoothDevice device) {
}
public void onBonding(BluetoothDevice device) {
}
/**
* 配对成功
* @param device
*/
public void onBondSuccess(BluetoothDevice device) {
this.device = device;
this.flag = true;
}
}
扫描可能会扫描出很多设备,你可以做一个页面把所有的设备显示出来
然后在页面操作后台进行连接调用 可以参考这个
https://blog.csdn.net/CrackgmKey/article/details/78909346
因为我的只需要连接固定蓝牙,我这样写的
先搜索已配对设备,是否有 HC-06 这个我需要的蓝牙,有就直接不扫描了,没有在开启扫描去搜索
// 已配对的设备
boolean isNeed = true; //是否找到需要的蓝牙
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
if("HC-06".equals(device.getName())){
this.device = device;
pinBlueCallBack.setFlag(true);
isNeed = false;
}
}
}
if(isNeed){
boolean flag = scanBlue();
if(flag){
//开启线程去扫描蓝牙,并在扫描到之后配对
new bluetoothSearchThread().start();
}
}
//开启线程等待,只要配对成功就连接
new bluetoothPairThread().start();
/**
* 等待12秒,如果未搜索到蓝牙就提示,搜索到了就配对连接
*/
private class bluetoothSearchThread extends Thread {
@Override
public void run() {
super.run();
try {
Thread.sleep(12000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = new Message();
msg.what = 0;
String msgStr = "超时未搜索到蓝牙!";
if(scanBlueCallBack.getDevice() != null){
msgStr = "已搜索到蓝牙";
device = scanBlueCallBack.getDevice();
pin(device);
}
msg.obj = msgStr;
handler.sendMessage(msg);
}
}
/**
* 连接线程
*/
private class bluetoothPairThread extends Thread {
@Override
public void run() {
super.run();
// while(true){
// //退出时中断线程
// if (isBlueToothPairStop){
// break;
// }
//配对成功
if(pinBlueCallBack.isFlag()){
connect(device,connectBlueCallBack);
}
// }
}
}
/**
* 连接 (在配对之后调用)
* @param device
*/
public void connect(BluetoothDevice device, ConnectBlueCallBack callBack){
if (device == null){
Log.d(TAG, "bond device null");
return;
}
//连接之前把扫描关闭
if (mBluetoothAdapter.isDiscovering()){
mBluetoothAdapter.cancelDiscovery();
}
connectBlueTask = new ConnectBlueTask(callBack);
connectBlueTask.execute(device);
}
package com.bonus.sdsagmeasureapp.ui.adapter.BlueBooth;
/**
* ************************************
*
* @ClassName: ConnectBlueTask
* @Description: java类作用描述
* @Author: fl
* @CreateDate: 2021/6/10 21:26
* @Version: 1.0
* ************************************
*/
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.UUID;
/**连接线程
* Created by zqf on 2018/7/7.
*/
public class ConnectBlueTask extends AsyncTask<BluetoothDevice, Integer, BluetoothSocket> {
private static final String TAG = ConnectBlueTask.class.getName();
private BluetoothDevice bluetoothDevice;
private ConnectBlueCallBack callBack;
public ConnectBlueTask(ConnectBlueCallBack callBack){
this.callBack = callBack;
}
@Override
protected BluetoothSocket doInBackground(BluetoothDevice... bluetoothDevices) {
bluetoothDevice = bluetoothDevices[0];
BluetoothSocket socket = null;
try{
// Log.d(TAG,"开始连接socket,uuid:" + ClassicsBluetooth.UUID);
//方法一:uuid
// socket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
//方法二:反射
Method m = bluetoothDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
socket = (BluetoothSocket) m.invoke(bluetoothDevice, 1);
if (socket != null && !socket.isConnected()){
socket.connect();
}
}catch (IOException | NoSuchMethodException e){
Log.e(TAG,"socket连接失败");
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
Log.e(TAG,"socket关闭失败");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return socket;
}
@Override
protected void onPreExecute() {
Log.d(TAG,"开始连接");
if (callBack != null){
callBack.onStartConnect();
}
}
@Override
protected void onPostExecute(BluetoothSocket bluetoothSocket) {
if (bluetoothSocket != null && bluetoothSocket.isConnected()){
Log.d(TAG,"连接成功");
if (callBack != null){
callBack.onConnectSuccess(bluetoothDevice, bluetoothSocket);
}
}else {
Log.d(TAG,"连接失败");
if (callBack != null){
callBack.onConnectFail(bluetoothDevice);
}
}
}
}
package com.bonus.sdsagmeasureapp.ui.adapter.BlueBooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
/**
* ************************************
*
* @ClassName: ConnectBlueCallBack
* @Description: java类作用描述
* @Author: fl
* @CreateDate: 2021/6/10 21:40
* @Version: 1.0
* ************************************
*/
public class ConnectBlueCallBack {
private BluetoothDevice device;
private BluetoothSocket socket;
private BluetoothAdapter bluetoothAdapter;
public boolean flag = false;
public ConnectBlueCallBack() {
}
public ConnectBlueCallBack(BluetoothAdapter bluetoothAdapter) {
this.bluetoothAdapter = bluetoothAdapter;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public BluetoothAdapter getBluetoothAdapter() {
return bluetoothAdapter;
}
public void setBluetoothAdapter(BluetoothAdapter bluetoothAdapter) {
this.bluetoothAdapter = bluetoothAdapter;
}
public BluetoothDevice getDevice() {
return device;
}
public void setDevice(BluetoothDevice device) {
this.device = device;
}
public BluetoothSocket getSocket() {
return socket;
}
public void setSocket(BluetoothSocket socket) {
this.socket = socket;
}
public void onStartConnect() {
}
/**
* 连接成功
* @param bluetoothDevice 设备
* @param bluetoothSocket socket
*/
public void onConnectSuccess(BluetoothDevice bluetoothDevice, BluetoothSocket bluetoothSocket) {
setDevice(bluetoothDevice);
setSocket(bluetoothSocket);
flag = true;
}
/**
* 连接失败
* @param bluetoothDevice 设备
*/
public void onConnectFail(BluetoothDevice bluetoothDevice) {
}
}
配对的方法
public void pin(BluetoothDevice device){
if (device == null){
XToastUtils.info("未找到设备");
return;
}
//配对之前把扫描关闭
if (mBluetoothAdapter.isDiscovering()){
mBluetoothAdapter.cancelDiscovery();
}
Log.e(TAG, "需配对设备码:"+device.getBondState());
Log.e(TAG, "本机配对码:"+BluetoothDevice.BOND_NONE);
Log.e(TAG, "需配对设备名称:"+device.getName());
//判断设备是否配对,没有配对在配,配对了就不需要配了(一旦配对了,他们的码就会不一样)
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
Log.d(TAG, "attemp to bond:" + device.getName());
try {
Method createBondMethod = device.getClass().getMethod("createBond");
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
returnValue.booleanValue();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "attemp to bond fail!");
}
}
}
扫描的方法
/**
* 扫描的方法 返回true 扫描成功
* 通过接收广播获取扫描到的设备
* @return
*/
public boolean scanBlue(){
//当前是否在扫描,如果是就取消当前的扫描,重新扫描
if (mBluetoothAdapter.isDiscovering()){
mBluetoothAdapter.cancelDiscovery();
}
//此方法是个异步操作,一般搜索12秒
return mBluetoothAdapter.startDiscovery();
}
上面的代码就可以连接蓝牙了,选在开启线程监听蓝牙返回数据,这个监听线程可以一开始就开启
/**
* 是否启动蓝牙监听返回数据
*/
private class receiveThread extends Thread {
@Override
public void run() {
super.run();
while(true){
if(isReceive){
break;
}
if(isConnectBlue()){
isReceive = true;
try {
is = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
// connectBlueTask.cancel(true);
new readThread().start();
// new ReadTask(readCallBack,socket).execute();
Message msg = new Message();
msg.what=0;
msg.obj = "启动蓝牙监听线程";
handler.sendMessage(msg);
}
}
}
}
/**
* 蓝牙监听线程
*/
public class readThread extends Thread{
@Override
public void run() {
try {
while (true) {
Message msg = new Message();
int count = 0;
while (count == 0) {
count = is.available();
}
String message = "监听返回[";
byte buf[] = new byte[count];
if (buf != null) {
is.read(buf);
message += ByteUtil.bytesToHexString(buf) + "]";
}
msg.what=1;
msg.obj = message;
handler.sendMessage(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
判断蓝牙是否连接
/**
* 蓝牙是否连接
* @return
*/
public boolean isConnectBlue(){
if(socket == null){
socket = connectBlueCallBack.getSocket();
}
return socket != null && socket.isConnected();
}
给蓝牙发送数据
/**
* 提交数据
*/
private void submitBaseStationData() {
String order = "fix position "+171.33+" "+31.05+" "+15.6+"\r\n";
//使用蓝牙提交数据
//TODO
if(isConnectBlue()){
XToastUtils.info("蓝牙连接中");
// new WriteTask(writeCallBack,socket).execute(order);
try {
os = socket.getOutputStream();//获取输出流
if (os != null) {//判断输出流是否为空
os.write(order.getBytes("UTF-8"));
// os.write(order.getBytes());
}
os.flush();
// os.close();//关闭输出流
} catch (IOException e) {
e.printStackTrace();
}
}else{
XToastUtils.info("蓝牙已断开或蓝牙未连接");
}
}