Android BLE 藍牙開發(fā)入門

初識低功耗藍牙

Android 4.3(API Level 18)開始引入Bluetooth Low Energy(BLE僵井,低功耗藍牙)的核心功能并提供了相應的 API府喳, 應用程序通過這些 API 掃描藍牙設備、查詢 services滋将、讀寫設備的 characteristics(屬性特征)等操作。

Android BLE 使用的藍牙協(xié)議是 GATT 協(xié)議,有關該協(xié)議的詳細內容可以參見藍牙官方文檔徊哑。以下我引用一張官網(wǎng)的圖來大概說明 Android 開發(fā)中我們需要了解的一些 Bluetooth Low Energy 的專業(yè)術語。

藍牙協(xié)議圖

Service

一個低功耗藍牙設備可以定義許多 Service, Service 可以理解為一個功能的集合聪富。設備中每一個不同的 Service 都有一個 128 bit 的 UUID 作為這個 Service 的獨立標志莺丑。藍牙核心規(guī)范制定了兩種不同的UUID,一種是基本的UUID墩蔓,一種是代替基本UUID的16位UUID梢莽。所有的藍牙技術聯(lián)盟定義UUID共用了一個基本的UUID:
0x0000xxxx-0000-1000-8000-00805F9B34FB
為了進一步簡化基本UUID,每一個藍牙技術聯(lián)盟定義的屬性有一個唯一的16位UUID奸披,以代替上面的基本UUID的‘x’部分昏名。例如,心率測量特性使用0X2A37作為它的16位UUID源内,因此它完整的128位UUID為:
0x00002A37-0000-1000-8000-00805F9B34FB

Characteristic

在 Service 下面葡粒,又包括了許多的獨立數(shù)據(jù)項,我們把這些獨立的數(shù)據(jù)項稱作 Characteristic膜钓。同樣的嗽交,每一個 Characteristic 也有一個唯一的 UUID 作為標識符。在 Android 開發(fā)中颂斜,建立藍牙連接后夫壁,我們說的通過藍牙發(fā)送數(shù)據(jù)給外圍設備就是往這些 Characteristic 中的 Value 字段寫入數(shù)據(jù);外圍設備發(fā)送數(shù)據(jù)給手機就是監(jiān)聽這些 Charateristic 中的 Value 字段有沒有變化沃疮,如果發(fā)生了變化盒让,手機的 BLE API 就會收到一個監(jiān)聽的回調。

更詳細的內容可以參見
GATT Profile 簡介
通用屬性配置文件(GATT)及其服務司蔬,特性與屬性介紹
GATT specification
GATT Services
藍牙【GATT】協(xié)議介紹


Android BLE API 簡介

BluetoothAdapter
BluetoothAdapter 擁有基本的藍牙操作邑茄,例如開啟藍牙掃描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)實例化一個 BluetoothDevice 用于連接藍牙設備的操作等等俊啼。

BluetoothDevice
代表一個遠程藍牙設備肺缕。這個類可以讓你連接所代表的藍牙設備或者獲取一些有關它的信息,例如它的名字,地址和綁定狀態(tài)等等同木。

BluetoothGatt
這個類提供了 Bluetooth GATT 的基本功能浮梢。例如重新連接藍牙設備,發(fā)現(xiàn)藍牙設備的 Service 等等彤路。

BluetoothGattService
這一個類通過 BluetoothGatt#getService 獲得秕硝,如果當前服務不可見那么將返回一個 null。這一個類對應上面說過的 Service洲尊。我們可以通過這個類的 getCharacteristic(UUID uuid) 進一步獲取 Characteristic 實現(xiàn)藍牙數(shù)據(jù)的雙向傳輸远豺。

BluetoothGattCharacteristic
這個類對應上面提到的 Characteristic。通過這個類定義需要往外圍設備寫入的數(shù)據(jù)和讀取外圍設備發(fā)送過來的數(shù)據(jù)颊郎。


Android 藍牙開發(fā)示例

第一步:聲明所需要的權限

<uses-permission android:name="android.permission.BLUETOOTH"/> 使用藍牙所需要的權限
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> 使用掃描和設置藍牙的權限(申明這一個權限必須申明上面一個權限)

在Android5.0之前憋飞,是默認申請GPS硬件功能的。而在Android 5.0 之后姆吭,需要在manifest 中申明GPS硬件模塊功能的使用榛做。

    <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
    <uses-feature android:name="android.hardware.location.gps" />

在 Android 6.0 及以上,還需要打開位置權限内狸。如果應用沒有位置權限检眯,藍牙掃描功能不能使用(其它藍牙操作例如連接藍牙設備和寫入數(shù)據(jù)不受影響)。

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

第二步:連接藍牙前的初始化工作

在建立藍牙連接之前昆淡,需要確認設備支持 BLE锰瘸。如果支持,再確認藍牙是否開啟昂灵。如果藍牙沒有開啟避凝,可以使用 BLuetoothAdapter 類來開啟藍牙。

  1. 獲取 BluetoothAdapter
    private BluetoothAdapter mBluetoothAdapter;
        
    // Initializes Bluetooth adapter.
    final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();
  1. 如果檢測到藍牙沒有開啟眨补,嘗試開啟藍牙
    // Ensures Bluetooth is available on the device and it is enabled. If not,
    // displays a dialog requesting user permission to enable Bluetooth.
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }

第三步:掃描藍牙設備

外圍設備開啟藍牙后管削,會廣播出許多的關于該設備的數(shù)據(jù)信息,例如 mac 地址撑螺,uuid 等等含思。通過這些數(shù)據(jù)我們可以篩選出需要的設備。

在 BluetoothAdapter 中甘晤,我們可以看到有兩個掃描藍牙的方法含潘。第一個方法可以指定只掃描含有特定 UUID Service 的藍牙設備,第二個方法則是掃描全部藍牙設備线婚。

boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
boolean startLeScan(BluetoothAdapter.LeScanCallback callback)

開啟藍牙掃描

final BluetoothAdapter.LeScanCallback callback = new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
        bluetoothDeviceArrayList.add(device);
        Log.d(TAG, "run: scanning...");
    }
};

mBluetoothAdapter.startLeScan(callback);

在 LeScanCallback 回調的方法中遏弱,第一個參數(shù)是代表藍牙設備的類,可以通過這個類建立藍牙連接獲取關于這一個設備的一系列詳細的參數(shù)塞弊,例如名字腾窝,MAC 地址等等缀踪;第二個參數(shù)是藍牙的信號強弱指標,通過藍牙的信號指標虹脯,我們可以大概計算出藍牙設備離手機的距離。計算公式為:d = 10^((abs(RSSI) - A) / (10 * n))奏候;第三個參數(shù)是藍牙廣播出來的廣告數(shù)據(jù)循集。
當執(zhí)行上面的代碼之后,一旦發(fā)現(xiàn)藍牙設備蔗草,LeScanCallback 就會被回調咒彤,直到 stopLeScan 被調用。出現(xiàn)在回調中的設備會重復出現(xiàn)咒精,所以如果我們需要通過 BluetoothDevice 獲取外圍設備的地址手動過濾掉已經(jīng)發(fā)現(xiàn)的外圍設備镶柱。

停止藍牙掃描

void    stopLeScan(BluetoothAdapter.LeScanCallback callback)

通過調用 BluetoothAdapter#stopLeScan 可以停止正在進行的藍牙掃描。這里需要注意的是模叙,傳入的回調必須是開啟藍牙掃描時傳入的回調歇拆,否則藍牙掃描不會停止。

由于藍牙掃描的操作比較消耗手機的能量范咨。所以我們不能一直開著藍牙故觅,必須設置一段時間之后關閉藍牙掃描。示例代碼如下:

private void scanLeDevice(final boolean enable) {
    if (enable) {
        // Stops scanning after a pre-defined scan period.
        // 預先定義停止藍牙掃描的時間(因為藍牙掃描需要消耗較多的電量)
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mScanning = false;
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }
        }, SCAN_PERIOD);
        mScanning = true;

        // 定義一個回調接口供掃描結束處理
        mBluetoothAdapter.startLeScan(mLeScanCallback);
    } else {
        mScanning = false;
        mBluetoothAdapter.stopLeScan(mLeScanCallback);
    }
}

第四步:連接藍牙設備

連接藍牙設備可以通過 BluetoothDevice#ConnectGatt 方法連接渠啊,也可以通過 BluetoothGatt#connect 方法進行重新連接输吏。以下分別是兩個方法的官方說明:

BluetoothDevice#connectGatt
BluetoothGatt   connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)

第二個參數(shù)表示是否需要自動連接。如果設置為 true, 表示如果設備斷開了替蛉,會不斷的嘗試自動連接贯溅。設置為 false 表示只進行一次連接嘗試。
第三個參數(shù)是連接后進行的一系列操作的回調躲查,例如連接和斷開連接的回調它浅,發(fā)現(xiàn)服務的回調,成功寫入數(shù)據(jù)熙含,成功讀取數(shù)據(jù)的回調等等罚缕。

BluetoothGatt#connect
boolean connect()

調用這一個方法相當與調用 BluetoothDevice#connectGatt 且第二個參數(shù) autoConnect 設置為 true。

當調用藍牙的連接方法之后怎静,藍牙會異步執(zhí)行藍牙連接的操作邮弹,如果連接成功會回調 BluetoothGattCalbackl#onConnectionStateChange 方法。這個方法運行的線程是一個 Binder 線程蚓聘,所以不建議直接在這個線程處理耗時的任務腌乡,因為這可能導致藍牙相關的線程被阻塞。

void    onConnectionStateChange(BluetoothGatt gatt, int status, int newState)

這一個方法有三個參數(shù)夜牡,第一個就藍牙設備的 Gatt 服務連接類与纽。
第二個參數(shù)代表是否成功執(zhí)行了連接操作侣签,如果為 BluetoothGatt.GATT_SUCCESS 表示成功執(zhí)行連接操作,第三個參數(shù)才有效急迂,否則說明這次連接嘗試不成功影所。有時候,我們會遇到 status == 133 的情況僚碎,根據(jù)網(wǎng)上大部分人的說法猴娩,這是因為 Android 最多支持連接 6 到 7 個左右的藍牙設備,如果超出了這個數(shù)量就無法再連接了勺阐。所以當我們斷開藍牙設備的連接時卷中,還必須調用 BluetoothGatt#close 方法釋放連接資源。否則渊抽,在多次嘗試連接藍牙設備之后很快就會超出這一個限制蟆豫,導致出現(xiàn)這一個錯誤再也無法連接藍牙設備。
第三個參數(shù)代表當前設備的連接狀態(tài)懒闷,如果 newState == BluetoothProfile.STATE_CONNECTED 說明設備已經(jīng)連接十减,可以進行下一步的操作了(發(fā)現(xiàn)藍牙服務,也就是 Service)毛雇。當藍牙設備斷開連接時嫉称,這一個方法也會被回調,其中的 newState == BluetoothProfile.STATE_DISCONNECTED灵疮。

第五步:發(fā)現(xiàn)服務

在成功連接到藍牙設備之后才能進行這一個步驟织阅,也就是說在 BluetoothGattCalbackl#onConnectionStateChang 方法被成功回調且表示成功連接之后調用 BluetoothGatt#discoverService 這一個方法。當這一個方法被調用之后震捣,系統(tǒng)會異步執(zhí)行發(fā)現(xiàn)服務的過程荔棉,直到 BluetoothGattCallback#onServicesDiscovered 被系統(tǒng)回調之后,手機設備和藍牙設備才算是真正建立了可通信的連接蒿赢。

到這一步润樱,我們已經(jīng)成功和藍牙設備建立了可通信的連接,接下來就可以執(zhí)行相應的藍牙通信操作了羡棵,例如寫入數(shù)據(jù)壹若,讀取藍牙設備的數(shù)據(jù)等等。

讀取數(shù)據(jù)

當我們發(fā)現(xiàn)服務之后就可以通過 BluetoothGatt#getService 獲取 BluetoothGattService皂冰,接著通過 BluetoothGattService#getCharactristic 獲取 BluetoothGattCharactristic店展。
通過 BluetoothGattCharactristic#readCharacteristic 方法可以通知系統(tǒng)去讀取特定的數(shù)據(jù)。如果系統(tǒng)讀取到了藍牙設備發(fā)送過來的數(shù)據(jù)就會調用 BluetoothGattCallback#onCharacteristicRead 方法秃流。通過 BluetoothGattCharacteristic#getValue 可以讀取到藍牙設備的數(shù)據(jù)赂蕴。以下是代碼示例:

@Override
public void onCharacteristicRead(final BluetoothGatt gatt,
                                    final BluetoothGattCharacteristic characteristic,
                                    final int status) {

    Log.d(TAG, "callback characteristic read status " + status
            + " in thread " + Thread.currentThread());
    if (status == BluetoothGatt.GATT_SUCCESS) {
        Log.d(TAG, "read value: " + characteristic.getValue());
    }

}


// 讀取數(shù)據(jù)
BluetoothGattService service = gattt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = gatt.getCharacteristic(CHARACTER_UUID);
gatt.readCharacteristic();

寫入數(shù)據(jù)

和讀取數(shù)據(jù)一樣,在執(zhí)行寫入數(shù)據(jù)前需要獲取到 BluetoothGattCharactristic舶胀。接著執(zhí)行一下步驟:

  1. 調用 BluetoothGattCharactristic#setValue 傳入需要寫入的數(shù)據(jù)(藍牙最多單次1支持 20 個字節(jié)數(shù)據(jù)的傳輸概说,如果需要傳輸?shù)臄?shù)據(jù)大于這一個字節(jié)則需要分包傳輸)碧注。
  2. 調用 BluetoothGattCharactristic#writeCharacteristic 方法通知系統(tǒng)異步往設備寫入數(shù)據(jù)。
  3. 系統(tǒng)回調 BluetoothGattCallback#onCharacteristicWrite 方法通知數(shù)據(jù)已經(jīng)完成寫入糖赔。此時萍丐,我們需要執(zhí)行 BluetoothGattCharactristic#getValue 方法檢查一下寫入的數(shù)據(jù)是否我們需要發(fā)送的數(shù)據(jù),如果不是按照項目的需要判斷是否需要重發(fā)放典。
    以下是示例代碼:
@Override
public void onCharacteristicWrite(final BluetoothGatt gatt,
                                    final BluetoothGattCharacteristic characteristic,
                                    final int status) {
    Log.d(TAG, "callback characteristic write in thread " + Thread.currentThread());
    if(!characteristic.getValue().equal(sendValue)) {
        // 執(zhí)行重發(fā)策略
        gatt.writeCharacteristic(characteristic);
    }
}

//往藍牙數(shù)據(jù)通道的寫入數(shù)據(jù)
BluetoothGattService service = gattt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = gatt.getCharacteristic(CHARACTER_UUID);
characteristic.setValue(sendValue);
gatt.writeCharacteristic(characteristic);

向藍牙設備注冊監(jiān)聽實現(xiàn)實時讀取藍牙設備的數(shù)據(jù)

BLE app通常需要獲取設備中characteristic 變化的通知碉纺。下面的代碼演示了怎么為一個Characteristic 設置一個監(jiān)聽。

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
        UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);

值得注意的是刻撒,除了通過 BluetoothGatt#setCharacteristicNotification 開啟 Android 端接收通知的開關,還需要往 Characteristic 的 Descriptor 屬性寫入開啟通知的數(shù)據(jù)開關使得當硬件的數(shù)據(jù)改變時耿导,主動往手機發(fā)送數(shù)據(jù)声怔。

最后一步:斷開連接

當我們連接藍牙設備完成一系列的藍牙操作之后就可以斷開藍牙設備的連接了。通過 BluetoothGatt#disconnect 可以斷開正在連接的藍牙設備舱呻。當這一個方法被調用之后醋火,系統(tǒng)會異步回調 BluetoothGattCallback#onConnectionStateChange 方法。通過這個方法的 newState 參數(shù)可以判斷是連接成功還是斷開成功的回調箱吕。
由于 Android 藍牙連接設備的資源有限芥驳,當我們執(zhí)行斷開藍牙操作之后必須執(zhí)行 BluetoothGatt#close 方法釋放資源。需要注意的是通過 BluetoothGatt#close 方法也可以執(zhí)行斷開藍牙的操作茬高,不過 BluetoothGattCallback#onConnectionStateChange 將不會收到任何回調兆旬。此時如果執(zhí)行 BluetoothGatt#connect 方法會得到一個藍牙 API 的空指針異常。所以怎栽,我們推薦的寫法是當藍牙成功連接之后丽猬,通過 BluetoothGatt#disconnect 斷開藍牙的連接,緊接著在 BluetoothGattCallback#onConnectionStateChange 執(zhí)行 BluetoothGatt#close 方法釋放資源熏瞄。
以下是代碼示例:

    @Override
    public void onConnectionStateChange(final BluetoothGatt gatt, final int status,
                                    final int newState) {
        Log.d(TAG, "onConnectionStateChange: thread "
                + Thread.currentThread() + " status " + newState);

        if (status != BluetoothGatt.GATT_SUCCESS) {
            String err = "Cannot connect device with error status: " + status;
      // 當嘗試連接失敗的時候調用 disconnect 方法是不會引起這個方法回調的脚祟,所以這里
                //   直接回調就可以了。
            gatt.close();
            Log.e(TAG, err);
            return;
        }

        if (newState == BluetoothProfile.STATE_CONNECTED) {
            gatt.discoverService();
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            gatt.close();
        }
    }

藍牙操作的注意事項

  1. 藍牙的寫入操作( 包括 Descriptor 的寫入操作), 讀取操作必須序列化進行. 寫入數(shù)據(jù)和讀取數(shù)據(jù)是不能同時進行的, 如果調用了寫入數(shù)據(jù)的方法, 馬上調用又調用寫入數(shù)據(jù)或者讀取數(shù)據(jù)的方法,第二次調用的方法會立即返回 false, 代表當前無法進行操作. 詳情可以參考 藍牙讀寫操作返回 false强饮,為什么多次讀寫只有一次回調由桌?
  2. Android 連接外圍設備的數(shù)量有限,當不需要連接藍牙設備的時候邮丰,必須調用 BluetoothGatt#close 方法釋放資源行您。詳細的參考可以看這里 Android BLE 藍牙開發(fā)的各種坑
  3. 藍牙 API 連接藍牙設備的超時時間大概在 20s 左右,具體時間看系統(tǒng)實現(xiàn)柠座。有時候某些設備進行藍牙連接的時間會很長邑雅,大概十多秒。如果自己手動設置了連接超時時間(例如通過 Handler#postDelay 設置了 5s 后沒有進入 BluetoothGattCallback#onConnectionStateChange 就執(zhí)行 BluetoothGatt#close 操作強制釋放斷開連接釋放資源)在某些設備上可能會導致接下來幾次的連接嘗試都會在 BluetoothGattCallback#onConnectionStateChange 返回 state == 133妈经。另外可以參考這篇吐槽 Android 中 BLE 連接出現(xiàn)“BluetoothGatt status 133”的解決方法
  4. 所有的藍牙操作使用 Handler 固定在一條線程操作淮野,這樣能省去很多因為線程不同步導致的麻煩

最后捧书,附上我寫的一個使用 RxJava 封裝藍牙操作庫,大家可以參考我的類庫源碼根據(jù)自己項目的需求二次開發(fā)骤星。https://github.com/Belolme/RxBLE

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末经瓷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子洞难,更是在濱河造成了極大的恐慌舆吮,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件队贱,死亡現(xiàn)場離奇詭異色冀,居然都是意外死亡,警方通過查閱死者的電腦和手機柱嫌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門锋恬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人编丘,你說我怎么就攤上這事与学。” “怎么了嘉抓?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵索守,是天一觀的道長。 經(jīng)常有香客問我抑片,道長卵佛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任蓝丙,我火速辦了婚禮级遭,結果婚禮上,老公的妹妹穿的比我還像新娘渺尘。我一直安慰自己挫鸽,他們只是感情好,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布鸥跟。 她就那樣靜靜地躺著丢郊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪医咨。 梳的紋絲不亂的頭發(fā)上枫匾,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音拟淮,去河邊找鬼干茉。 笑死,一個胖子當著我的面吹牛很泊,可吹牛的內容都是我干的角虫。 我是一名探鬼主播沾谓,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼戳鹅!你這毒婦竟也來了均驶?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤枫虏,失蹤者是張志新(化名)和其女友劉穎妇穴,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體隶债,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡腾它,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了死讹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片携狭。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖回俐,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情稀并,我是刑警寧澤仅颇,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站碘举,受9級特大地震影響忘瓦,放射性物質發(fā)生泄漏。R本人自食惡果不足惜引颈,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一耕皮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蝙场,春花似錦凌停、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至完箩,卻和暖如春赐俗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弊知。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工阻逮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秩彤。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓叔扼,卻偏偏與公主長得像事哭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子币励,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • BLE 與經(jīng)典藍牙的區(qū)別 BLE 的 Kotlin 下實踐 BluetoothGattCallback 不回調異常...
    chauI閱讀 10,869評論 1 7
  • 藍牙——BLE 介紹 1.BLE 是 Bluetooth Low Energy 的縮寫慷蠕,意思為低功耗藍牙。由藍牙技...
    Kip_Salens閱讀 2,182評論 1 3
  • 相關概念 BRBasic Rate食呻,早期的傳統(tǒng)藍牙技術 V1.1流炕, V1.2 版本,傳輸速率為748~810kb/...
    七零八落問號閱讀 9,403評論 2 26
  • 因為自己的項目中有用到了藍牙相關的功能仅胞,所以之前也斷斷續(xù)續(xù)地針對藍牙通信尤其是BLE通信進行了一番探索每辟,整理出了一...
    陳利健閱讀 114,599評論 172 294
  • 在家待了三個月,終于還是到了離開家門的時候干旧。 三個月渠欺,對于我這樣貪戀家的人來說,就像是莫大的恩賜椎眯,這是一段很快樂的...
    果安安閱讀 324評論 0 1