Android藍牙開發前,首先要區分是經典藍牙開發仍是BLE(低功耗)藍牙開發,它們的開發是有區別的,若是還分不清經典藍牙和BLE(低功耗)藍牙的小夥伴,能夠先看Android藍牙開發—經典藍牙和BLE(低功耗)藍牙的區別android
本文是針對經典藍牙開發的,若是是BLE(低功耗)藍牙開發,能夠看Android藍牙開發—BLE(低功耗)藍牙詳細開發流程
開發流程app
開啓藍牙
掃描藍牙
配對藍牙
鏈接藍牙
通訊異步
開啓藍牙socket
1.獲取BluetoothAdapter對象ide
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();函數
2.判斷設備是否支持藍牙學習
/**
* 設備是否支持藍牙 true爲支持
* @return
*/
public boolean isSupportBlue(){
return mBluetoothAdapter != null;
}ui
3.判斷藍牙是否開啓this
/**
* 藍牙是否打開 true爲打開
* @return
*/
public boolean isBlueEnable(){
return isSupportBlue() && mBluetoothAdapter.isEnabled();
}.net
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下載。如有不當之處,請留言討論,一塊兒學習進步。