Android蓝牙开发—经典蓝牙详细开发流程
16lz
2022-06-24
Android蓝牙开发前,首先要区分是经典蓝牙开发还是BLE(低功耗)蓝牙开发,它们的开发是有区别的,如果还分不清经典蓝牙和BLE(低功耗)蓝牙的小伙伴,可以先看Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别
本文是针对经典蓝牙开发的,如果是BLE(低功耗)蓝牙开发,可以看Android蓝牙开发—BLE(低功耗)蓝牙详细开发流程
开发流程
- 开启蓝牙
- 扫描蓝牙
- 配对蓝牙
- 连接蓝牙
- 通信
开启蓝牙
1.获取BluetoothAdapter对象
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
2.判断设备是否支持蓝牙
/** * 设备是否支持蓝牙 true为支持 * @return */public boolean isSupportBlue(){ return mBluetoothAdapter != null;}
3.判断蓝牙是否开启
/** * 蓝牙是否打开 true为打开 * @return */public boolean isBlueEnable(){ return isSupportBlue() && mBluetoothAdapter.isEnabled();}
4.开启蓝牙
- 异步自动开启蓝牙
/** * 自动打开蓝牙(异步:蓝牙不会立刻就处于开启状态) * 这个方法打开蓝牙不会弹出提示 */public void openBlueAsyn(){ if (isSupportBlue()) { mBluetoothAdapter.enable(); }}
- 同步提示开启蓝牙
/** * 自动打开蓝牙(同步) * 这个方法打开蓝牙会弹出提示 * 需要在onActivityResult 方法中判断resultCode == RESULT_OK true为成功 */public void openBlueSync(Activity activity, int requestCode){ Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); activity.startActivityForResult(intent, requestCode);}
5.权限处理
- 处理6.0以下版本的权限
在AndroidManifest里面添加权限
- 处理6.0以上版本的权限
(1)在AndroidManifest里面添加权限
(2)动态检查权限
/** * 检查权限 */private void checkPermissions() { String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION}; List permissionDeniedList = new ArrayList<>(); for (String permission : permissions) { int permissionCheck = ContextCompat.checkSelfPermission(this, permission); if (permissionCheck == PackageManager.PERMISSION_GRANTED) { onPermissionGranted(permission); } else { permissionDeniedList.add(permission); } } if (!permissionDeniedList.isEmpty()) { String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]); ActivityCompat.requestPermissions(this, deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION); }}/** * 权限回调 * @param requestCode * @param permissions * @param grantResults */@Overridepublic final void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case REQUEST_CODE_PERMISSION_LOCATION: if (grantResults.length > 0) { for (int i = 0; i < grantResults.length; i++) { if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { onPermissionGranted(permissions[i]); } } } break; }}
(3)开启GPS
/** * 开启GPS * @param permission */private void onPermissionGranted(String permission) { switch (permission) { case Manifest.permission.ACCESS_FINE_LOCATION: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !checkGPSIsOpen()) { new AlertDialog.Builder(this) .setTitle("提示") .setMessage("当前手机扫描蓝牙需要打开定位功能。") .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setPositiveButton("前往设置", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivityForResult(intent, REQUEST_CODE_OPEN_GPS); } }) .setCancelable(false) .show(); } else { //GPS已经开启了 } break; }}
(4)检查GPS是否开启
/** * 检查GPS是否打开 * @return */private boolean checkGPSIsOpen() { LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); if (locationManager == null) return false; return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);}
扫描蓝牙
1.扫描周围蓝牙设备(配对上的设备有可能扫描不出来)
/** * 扫描的方法 返回true 扫描成功 * 通过接收广播获取扫描到的设备 * @return */public boolean scanBlue(){ if (!isBlueEnable()){ Log.e(TAG, "Bluetooth not enable!"); return false; } //当前是否在扫描,如果是就取消当前的扫描,重新扫描 if (mBluetoothAdapter.isDiscovering()){ mBluetoothAdapter.cancelDiscovery(); } //此方法是个异步操作,一般搜索12秒 return mBluetoothAdapter.startDiscovery();}
2.取消扫描蓝牙
/** * 取消扫描蓝牙 * @return true 为取消成功 */public boolean cancelScanBule(){ if (isSupportBlue()){ return mBluetoothAdapter.cancelDiscovery(); } return true;}
3.通过广播的方式接收扫描结果
(1)注册广播
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);
(2)接收广播
/** *扫描广播接收类 * Created by zqf on 2018/7/6. */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); 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; } }}
配对蓝牙
1.开始配对
/** * 配对(配对成功与失败通过广播返回) * @param device */public void pin(BluetoothDevice device){ if (device == null){ Log.e(TAG, "bond device null"); return; } if (!isBlueEnable()){ Log.e(TAG, "Bluetooth not enable!"); return; } //配对之前把扫描关闭 if (mBluetoothAdapter.isDiscovering()){ mBluetoothAdapter.cancelDiscovery(); } //判断设备是否配对,没有配对在配,配对了就不需要配了 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) { // TODO Auto-generated catch block e.printStackTrace(); Log.e(TAG, "attemp to bond fail!"); } }}
2.取消配对
/** * 取消配对(取消配对成功与失败通过广播返回 也就是配对失败) * @param device */public void cancelPinBule(BluetoothDevice device){ if (device == null){ Log.d(TAG, "cancel bond device null"); return; } if (!isBlueEnable()){ Log.e(TAG, "Bluetooth not enable!"); return; } //判断设备是否配对,没有配对就不用取消了 if (device.getBondState() != BluetoothDevice.BOND_NONE) { Log.d(TAG, "attemp to cancel bond:" + device.getName()); try { Method removeBondMethod = device.getClass().getMethod("removeBond"); Boolean returnValue = (Boolean) removeBondMethod.invoke(device); returnValue.booleanValue(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e(TAG, "attemp to cancel bond fail!"); } }}
3.通过广播的方式接收配对结果
(1)注册广播
IntentFilter filter4 = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);IntentFilter filter5 = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);registerReceiver(pinBlueReceiver,filter4);registerReceiver(pinBlueReceiver,filter5);
(2)接收广播
/**配对广播接收类 * Created by zqf on 2018/7/7. */public class PinBlueReceiver extends BroadcastReceiver { private String pin = "0000"; //此处为你要连接的蓝牙设备的初始密钥,一般为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[]{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; } } }}
连接蓝牙
经典蓝牙连接相当于socket连接,是个非常耗时的操作,所以应该放到子线程中去完成。
1.连接线程
/**连接线程 * Created by zqf on 2018/7/7. */public class ConnectBlueTask extends AsyncTask { 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); socket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(ClassicsBluetooth.UUID)); if (socket != null && !socket.isConnected()){ socket.connect(); } }catch (IOException e){ Log.e(TAG,"socket连接失败"); try { socket.close(); } catch (IOException e1) { e1.printStackTrace(); Log.e(TAG,"socket关闭失败"); } } 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, "连接失败"); } }}
2.启动连接线程
/** * 连接 (在配对之后调用) * @param device */public void connect(BluetoothDevice device, ConnectBlueCallBack callBack){ if (device == null){ Log.d(TAG, "bond device null"); return; } if (!isBlueEnable()){ Log.e(TAG, "Bluetooth not enable!"); return; } //连接之前把扫描关闭 if (mBluetoothAdapter.isDiscovering()){ mBluetoothAdapter.cancelDiscovery(); } new ConnectBlueTask(callBack).execute(device);}
3.判断是否连接成功
/** * 蓝牙是否连接 * @return */public boolean isConnectBlue(){ return mBluetoothSocket != null && mBluetoothSocket.isConnected();}
4.断开连接
/** * 断开连接 * @return */public boolean cancelConnect(){ if (mBluetoothSocket != null && mBluetoothSocket.isConnected()){ try { mBluetoothSocket.close(); } catch (IOException e) { e.printStackTrace(); return false; } } mBluetoothSocket = null; return true;}
5.MAC地址连接
/** * 输入mac地址进行自动配对 * 前提是系统保存了该地址的对象 * @param address * @param callBack */public void connectMAC(String address, ConnectBlueCallBack callBack) { if (!isBlueEnable()){ return ; } BluetoothDevice btDev = mBluetoothAdapter.getRemoteDevice(address); connect(btDev, callBack);}
通信
1.读取数据线程
/**读取线程 * Created by zqf on 2018/7/7. */public class ReadTask extends AsyncTask { private static final String TAG = ReadTask.class.getName(); private ReadCallBack callBack; private BluetoothSocket socket; public ReadTask(ReadCallBack callBack, BluetoothSocket socket){ this.callBack = callBack; this.socket = socket; } @Override protected String doInBackground(String... strings) { BufferedInputStream in = null; try { StringBuffer sb = new StringBuffer(); in = new BufferedInputStream(socket.getInputStream()); int length = 0; byte[] buf = new byte[1024]; while ((length = in.read()) != -1) { sb.append(new String(buf,0,length)); } return sb.toString(); } catch (IOException e) { e.printStackTrace(); }finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } return "读取失败"; } @Override protected void onPreExecute() { Log.d(TAG,"开始读取数据"); if (callBack != null) callBack.onStarted(); } @Override protected void onPostExecute(String s) { Log.d(TAG,"完成读取数据"); if (callBack != null){ if ("读取失败".equals(s)){ callBack.onFinished(false, s); }else { callBack.onFinished(true, s); } } }}
2.写入数据线程
/**写入线程 * Created by zqf on 2018/7/7. */public class WriteTask extends AsyncTask{ private static final String TAG = WriteTask.class.getName(); private WriteCallBack callBack; private BluetoothSocket socket; public WriteTask(WriteCallBack callBack, BluetoothSocket socket){ this.callBack = callBack; this.socket = socket; } @Override protected String doInBackground(String... strings) { String string = strings[0]; OutputStream outputStream = null; try{ outputStream = socket.getOutputStream(); outputStream.write(string.getBytes()); } catch (IOException e) { Log.e("error", "ON RESUME: Exception during write.", e); return "发送失败"; }finally { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } return "发送成功"; } @Override protected void onPreExecute() { if (callBack != null) callBack.onStarted(); } @Override protected void onPostExecute(String s) { if (callBack != null){ if ("发送成功".equals(s)){ callBack.onFinished(true, s); }else { callBack.onFinished(false, s); } } }}
以上就是经典蓝牙的开发流程和部分代码,后期会提供demo下载。若有不当之处,请留言讨论,一起学习进步。
更多相关文章
- android IBeacon 开发(一)搜索IBeacon基站
- android 蓝牙开发
- Android音频系统之USB设备通路(Android(安卓)5.1)
- 条码扫描二维码扫描——ZXing android 源码简化
- android zbar二维码扫描(中文不乱码)
- Settings中蓝牙连接流程
- Android(安卓)8 蓝牙打开过程
- android蓝牙开发————实现服务端客户端通信
- Android获取本机蓝牙地址