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里面添加权限
<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
处理6.0以上版本的权限
(1)在AndroidManifest里面添加权限
<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!--模糊定位权限,仅作用于6.0+-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--精准定位权限,仅作用于6.0+-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
(2)动态检查权限
/**
* 检查权限
*/
private void checkPermissions() {
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
List<String> 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<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);
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<String, Integer, String> {
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<String, Integer, String>{
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下载。若有不当之处,请留言讨论,一起学习进步。
来源:oschina
链接:https://my.oschina.net/u/920274/blog/2998607