Android Bluetooth 入門

概述

藍牙(英語:Bluetooth),一種無線通信技術標準,用來讓固定與移動設備,在短距離間交換數據,以造成我的局域網(PAN)。藍牙技術當前由藍牙技術聯盟(SIG)來負責維護其技術標準。java

版本

圖片

分類

藍牙可分爲經典藍牙(Classic Bluetooth)、低功耗藍牙(Bluetooth Low Energy)、雙模藍牙三大類。2009 年發佈的藍牙 3.0 及以前的藍牙版本包括 BR、EDR 和 HS(AMP) 三種藍牙技術,統稱爲經典藍牙技術,只支持經典藍牙技術的藍牙稱爲經典藍牙。2010 年 SIG 聯盟合併了 Wibree 聯盟,並把 Wibree 聯盟提出的低功耗無線技術從新命名爲低功耗藍牙技術(BLE)。2010 年發佈的藍牙 4.0 規格就同時包含經典藍牙和低功耗藍牙,只支持低功耗藍牙技術的藍牙稱爲低功率藍牙,同時支持經典藍牙和低功率藍牙技術的藍牙稱爲雙模藍牙。低功耗藍牙與經典藍牙技術是不兼容的,因此低功耗藍牙和經典藍牙二者之間是不能相互通訊的。android

優缺點

經典藍牙技術持續保持鏈接,能夠傳輸大數據,適合文件傳輸、音頻播放等,兼容性高,廣播信道多,缺點是鏈接成本高,功耗高;低功耗藍牙鏈接速度快,低功耗,廣播信道少,缺點是數據傳輸量有限制。git

Android

Android 平臺包含藍牙網絡堆棧支持,憑藉此項支持,設備能以無線方式與其餘藍牙設備交換數據。應用框架提供了經過 Android Bluetooth API 訪問藍牙功能的途徑。針對具備低功耗要求的藍牙設備,Android 4.3(API 級別 18)中引入了面向低功耗藍牙的 API 支持。即 SPP 協議(經典藍牙)和 GATT 協議(低功耗藍牙)的鏈接方式。 使用 Android 藍牙功能須要在 Manifest.xml 文件中聲明藍牙權限,若是使用了藍牙掃描功能還須要聲明位置權限,由於藍牙是具備定位功能的,目標版本是 Android 23 及以上版本須要添加動態權限申請。github

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
// 藍牙掃描須要位置權限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
複製代碼

SPP 方式

  1. 啓用藍牙
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
複製代碼
  1. 查詢配對的設備
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}
複製代碼
  1. 發現設備,這裏須要註冊 ACTION_FOUND Intent BroadcastReceiver, Intent 攜帶額外字段 EXTRA_DEVICE 和 EXTRA_CLASS,兩者分別包含 BluetoothDevice 和 BluetoothClass,若是沒申請位置權限,這裏發現不了其餘設備。
mBluetoothAdapter.startDiscovery();
// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
複製代碼
  1. 鏈接設備,藍牙鏈接屬於耗時操做,須要放在子線程執行。在藍牙協議中,UUID 被用來標識藍牙設備所提供的服務,並不是是標識藍牙設備自己。一個藍牙設備能夠提供多種服務,好比 A2DP(藍牙音頻傳輸)、HEADFREE(免提)、PBAP(電話本)等,每種服務都對應一個UUID。鏈接成功後能夠獲取藍牙設備的輸入輸出流,進行數據交互操做。
private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;

        // 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(MY_UUID);
        } 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();
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } 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);
    }

    /** Will cancel an in-progress connection, and close the socket */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}
複製代碼

GATT 方式

  1. 啓用藍牙和 SPP 方式是同樣的
  2. 發現設備,一樣須要在子線程執行,不過經過接收回調的方式獲取發現的設備,而不是註冊廣播。不過須要注意的是發現設備是高功耗行爲,須要設置一個超時時間中止掃描。
private ScanCallback mLeScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
    }

    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
    }
};

 mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
        bluetoothLeScanner.stopScan(mLeScanCallback);
    }
  }, SCAN_PERIOD);
 bluetoothLeScanner.startScan(mLeScanCallback);
複製代碼
  1. 鏈接設備,能夠在 BluetoothGattCallback 中實現各類回調方法,如發現服務、監聽藍牙數據等。值得注意的一點是鏈接 API 在 Android version >= 23 時多了一個參數,若是不加上 BluetoothDevice.TRANSPORT_LE 可能致使鏈接不上 BLE 設備
// Various callback methods defined by the BLE API.
    private final BluetoothGattCallback gattCallback =
            new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,                 int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                connectionState = STATE_CONNECTED;
                Log.i(TAG, "Connected to GATT server.");
                Log.i(TAG, "Attempting to start service discovery:" +
                        bluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                connectionState = STATE_DISCONNECTED;
                Log.i(TAG, "Disconnected from GATT server.");
            }
        }

        @Override
        // New services discovered
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        // Result of a characteristic read operation
        public void onCharacteristicRead(BluetoothGatt gatt,                 BluetoothGattCharacteristic characteristic,                 int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }
     ...
    };
BluetoothGatt bluetoothGatt;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
     bluetoothGatt = device.connectGatt(context, false, mGattCallback, BluetoothDevice.TRANSPORT_LE);
} else {
   bluetoothGatt = device.connectGatt(context, false, mGattCallback);
}
複製代碼

總結

其實 Android 藍牙相關的 API 使用仍是比較簡單的,對於初次接觸藍牙開發的小白來講,弄清楚藍牙設備的類型以及鏈接方式就成功了一半(經典藍牙 SPP,低功耗藍牙 GATT)。另一半在於藍牙設備的適配及細節的掌握,適配問題能夠多聯繫藍牙設備供應商的技術支持,細節問題就靠平時積累和多搜索查閱資料了。 最後安利兩個 BLE 開源庫:RxAndroidBleflutter_blue網絡

@123lxw123, 本文版權屬於再惠研發團隊,歡迎轉載,轉載請保留出處。app

相關文章
相關標籤/搜索