Android蓝牙开发—经典蓝牙详细开发流程
发布时间:2018-07-16 13:41,
浏览次数:637
, 标签:
Android
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里面添加权限
/>
android:name="android.permission.BLUETOOTH_ADMIN" />
* 处理6.0以上版本的权限
(1)在AndroidManifest里面添加权限
/>
android:name="android.permission.BLUETOOTH_ADMIN" />
android:name="android.permission.ACCESS_FINE_LOCATION" />
(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 */ @Override public
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下载。若有不当之处,请留言讨论,一起学习进步。