Android藍芽開發流程實踐
概述
工作需要用到Android藍芽開發,所以在這裡對Android藍芽開發做一個整理。
先了解下Android藍芽開發的基礎知識:官方文件戳這裡
我們需要知道,現在的藍芽分為經典藍芽和BLE(低功耗藍芽)兩種,經典藍芽和低功耗藍芽的區別戳這裡 ,經典藍芽適合傳輸資料量比較大的傳輸,比如影象、視訊、音樂等,耗電也大;BLE適合耗電小,實時性要求高和資料量小的傳輸,比如智慧穿戴裝置、遙控類、滑鼠、鍵盤、感測裝置如心跳帶,血壓計,溫度感測器等。
對於Android開發者來說,我們要知道 Android 4.3 及以上版本才支援BLE,常說的藍芽單模和雙模指的是僅支援BLE和同時支援BLE和經典藍芽,經典藍芽和BLE之間不能通訊,Android手機同時支援經典藍芽和BLE,但是掃描藍芽的時候,只能掃描其中一種,如果是Android手機跟其他裝置通過藍芽通訊,首先要確認裝置支援的藍芽協議。下面記錄的是經典藍芽開發步驟。
藍芽開發步驟
通常Android藍芽開發包含以下5個步驟:
- 開啟
- 掃描
- 配對
- 連線
- 通訊
開啟藍芽
- 許可權(需要藍芽和GPS許可權,Android 6.0以上要加上執行時許可權授權)
在 AndroidManifest 中宣告許可權:
<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 android:name="android.permission.ACCESS_FINE_LOCATION" />
獲取定位授權:
//要模糊定位許可權才能搜尋到藍芽
PermissionUtil.requestEach(this, new PermissionUtil.OnPermissionListener() {
@Override
public void onSucceed() {
//授權成功後開啟藍芽
openBlueTooth();
}
@Override
public void onFailed(boolean showAgain) {
}
}, PermissionUtil.LOCATION);
- 判斷裝置是否支援藍芽
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
//如果mBluetoothAdapter為null,該裝置不支援藍芽,不過基本上都支援的
}
- 判斷藍芽是否開啟,沒有就開啟
if (!mBluetoothAdapter.isEnabled()) {
//若沒開啟則開啟藍芽
mBluetoothAdapter.enable();
}
掃描藍芽
- 可以掃描有指定 UUID 服務的藍芽裝置,UUID 不是裝置的標識,而是某個服務的標識, 什麼是UUID戳這裡
- 可以掃描全部藍芽裝置
- 注意:藍芽裝置被某裝置(包括當前的裝置)配對/連線後,可能不會再被掃描到
//掃描:經典藍芽
mBluetoothAdapter.startDiscovery();
//掃描:低功耗藍芽,需要加上停止掃描規則,掃描到指定裝置或者一段時間後,這裡設定10秒後停止掃描
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(MainActivity.this);
Log.i(TAG, "=== 停止掃描了 === ");
}
}, SCAN_PERIOD);
mBluetoothAdapter.startLeScan(this);
通過廣播來監聽掃描結果:
//廣播接收器
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
//掃描開始
break;
case BluetoothDevice.ACTION_FOUND:
//發現藍芽
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
//掃描結束
break;
}
}
};
配對藍芽
- 配對與連線之間的區別:配對意味著兩個裝置之間知道彼此的存在,通過配對金鑰,建立一個加密的連線。而連線意味著裝置之間共享一個通訊通道(UUID),能夠彼此傳輸資料
/**
* 藍芽配對,配對結果通過廣播返回
* @param device
*/
public void pin(BluetoothDevice device) {
if (device == null || !mBluetoothAdapter.isEnabled()) {
return;
}
//配對之前把掃描關閉
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
//判斷裝置是否配對,沒有就進行配對
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
try {
Method createBondMethod = device.getClass().getMethod("createBond");
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
returnValue.booleanValue();
} catch (Exception e) {
e.printStackTrace();
}
}
}
廣播監聽配對結果:
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
//配對狀態變化廣播
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice
.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_NONE:
Log.i(TAG, "--- 配對失敗 ---");
break;
case BluetoothDevice.BOND_BONDING:
Log.i(TAG, "--- 配對中... ---");
break;
case BluetoothDevice.BOND_BONDED:
Log.i(TAG, "--- 配對成功 ---");
break;
}
break;
連線藍芽
- 配對成功之後,兩臺裝置之間建立連線,一臺充當client的角色,另一臺充當server的角色,由client發起連線;
- 通過 UUID 建立 BluetoothSocket 進行連線,兩個端的UUID要一致,client和server都自己開發的話,可以由服務端建立一個UUID
- 發起連線和監聽連線都要在子執行緒中執行
在client端的子執行緒中發起連線:
/**
*
* 發起藍芽連線的執行緒
* 作者: 程式碼來自於Google官方 -> API指南 -> 藍芽模組
* 日期: 18/12/14
*/
public class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private final BluetoothAdapter mBluetoothAdapter;
private ConnectCallBack callBack;
public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, ConnectCallBack callBack) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
mBluetoothAdapter = bluetoothAdapter;
this.callBack = callBack;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(UUID.fromString(ServerActivity.uuidStr));
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
// Do work to manage the connection (in a separate thread)
// manageConnectedSocket(mmSocket); //啟動資料傳輸的執行緒
if(callBack != null) {
callBack.onConnectSucceed(mmSocket);
}
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
public interface ConnectCallBack {
public void onConnectSucceed(BluetoothSocket serverSocket);
}
}
在server端的子執行緒中監聽連線:
/**
*
* 監聽藍芽連線執行緒
* 作者: 程式碼來自於Google官方 -> API指南 -> 藍芽模組
* 日期: 18/12/14
*/
public class AcceptThread extends Thread {
private static final String TAG = "BluetoothDemo";
private final BluetoothServerSocket mmServerSocket;
private AcceptCallBack callBack;
public AcceptThread(BluetoothAdapter bluetoothAdapter, AcceptCallBack callBack) {
this.callBack = callBack;
// Use a temporary object that is later assigned to mmServerSocket,
// because mmServerSocket is final
BluetoothServerSocket tmp = null;
try {
// MY_UUID is the app's UUID string, also used by the client code
tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord("bluetoothdemo", UUID
.fromString(ServerActivity.uuidStr));
} catch (IOException e) {
}
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
while (true) {
Log.i(TAG, "AcceptThread監聽中...");
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
try {
// Do work to manage the connection (in a separate thread)
// manageConnectedSocket(socket); //啟動資料傳輸的執行緒
if(callBack != null) {
callBack.onAcceptSucceed(socket);
}
Log.i(TAG, "AcceptThread連線成功");
mmServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
/**
* Will cancel the listening socket, and cause the thread to finish
*/
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) {
}
}
public interface AcceptCallBack {
public void onAcceptSucceed(BluetoothSocket serverSocket);
}
}
通訊
- 連線成功後,通過socket得到I/O流,讀取資料和寫入資料,在兩個裝置之間傳輸資料。
/**
* 傳送和接收資料
* 作者: 程式碼來自於Google官方 -> API指南 -> 藍芽模組
* 日期: 18/12/14
*/
public class ConnectedThread extends Thread {
private static final String TAG = "ConnectedThread";
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private Handler mHandler;
public ConnectedThread(BluetoothSocket socket, Handler handler) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
mHandler = handler;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
if(mHandler != null) {
mHandler.obtainMessage(ServerActivity.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
}
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
ConnectThread、AcceptThread 和 ConnectedThread 都是Google官方文件裡面的示例,我自己加了些回撥方法而已,如果覺得比較亂,可以直接去看官方文件。官方文件戳這裡
程式碼上傳到了GitHub, 功能比較簡單,兩臺手機一個充當client,一個充當server,嚴格按照 開啟 >> 掃描 >> 配對 >> 連線 >> 通訊
5個步驟走才能實現通訊。僅供參考。
相關文章
- Android 開啟藍芽流程Android藍芽
- Android開發--藍芽操作Android藍芽
- Android 傳統藍芽開發Android藍芽
- Android 藍芽音響開發Android藍芽
- Android藍芽開發全面總結Android藍芽
- iOS藍芽開發 Bluetooth藍芽CoreBluetooth 藍芽中心裝置的實現 藍芽外設的實現 有DemoiOS藍芽
- iOS藍芽開發iOS藍芽
- 藍芽工作流程藍芽
- Android藍芽使用詳解(普通藍芽)Android藍芽
- Android 藍芽開發相關知識總結Android藍芽
- Android藍芽4.0(ble)開發的解決方案Android藍芽
- Android藍芽協議-藍芽掃描 startDiscoveryAndroid藍芽協議
- iOS 藍芽開發 - swift版iOS藍芽Swift
- 微信小程式藍芽開發微信小程式藍芽
- Android BLE 藍芽開發——掃碼槍基於BLESSEDAndroid藍芽
- Android藍芽那點事——深入瞭解藍芽BlE藍芽 《總結篇》Android藍芽
- iOS 藍芽開發·基礎篇iOS藍芽
- iOS藍芽4.0(BLE)開發(一)iOS藍芽
- Android:藍芽實現一對一聊天Android藍芽
- iOS藍芽Mesh開發總結一iOS藍芽
- iOS藍芽Mesh開發總結二iOS藍芽
- React Native 藍芽4.0 BLE開發React Native藍芽
- iOS藍芽4.0開發基礎教程iOS藍芽
- 安卓微信小程式開發之“藍芽”安卓微信小程式藍芽
- Android藍芽那點事——深入瞭解Android藍芽Bluetooth《進階篇》Android藍芽
- Android-藍芽聊天demoAndroid藍芽
- Android Ble藍芽入門Android藍芽
- Android 4.2藍芽介紹Android藍芽
- iOS 藍芽4.0開發使用(內附Demo)iOS藍芽
- iOS-BLE藍芽開發持續更新iOS藍芽
- BLE藍芽那些事—深入瞭解Android藍芽Bluetooth基礎篇藍芽Android
- Android裝置間實現藍芽共享上網Android藍芽
- android藍芽BLE(三) —— 廣播Android藍芽
- android藍芽BLE(二) —— 通訊Android藍芽
- android藍芽BLE(一) —— 掃描Android藍芽
- android 4.0 藍芽分析之一Android藍芽
- android 4.0 藍芽分析之二Android藍芽
- Android藍芽串列埠通訊Android藍芽串列埠