Android藍(lán)牙周邊設(shè)備開(kāi)發(fā)

本文只講述實(shí)際開(kāi)發(fā)中的最基本的概念、用法及代碼,不過(guò)多深入概念及源碼。

什么是周邊設(shè)備

  • BLE(藍(lán)牙4.0+) 有兩種狀態(tài)模式:中心(center)及peripheral(周邊)。
  • 大多數(shù)中心設(shè)備的扮演者是手機(jī)、電腦等能主動(dòng)去連接別人的設(shè)備,而大多數(shù)周邊設(shè)備就等著這些中心設(shè)備連接,如手環(huán)、血糖儀等。
  • Android從 Lolipop 開(kāi)始支持了BLE Peripheral(周邊設(shè)備)開(kāi)發(fā)。
  • 如果你希望使Android設(shè)備開(kāi)啟為周邊設(shè)備模式,請(qǐng)往下看。

配置

AndroidManifest.xml 中 添加以下權(quán)限:

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <!-- 6.0之后藍(lán)牙還需要地理位置權(quán)限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!-- 只掃描ble設(shè)備 -->
    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />

開(kāi)啟藍(lán)牙

// 是否支持ble
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(mContext, "hasSystemFeature == false", Toast.LENGTH_SHORT).show();
    return false;
}
// 是否能獲取到藍(lán)牙服務(wù)
mBluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
    Toast.makeText(mContext, "mBluetoothManager == null", Toast.LENGTH_LONG).show();
    return false;
}
// 獲取藍(lán)牙適配器
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
    Toast.makeText(mContext, "BluetoothAdapter == null", Toast.LENGTH_LONG).show();
    return false;
}
// 藍(lán)牙是否打開(kāi)
if (!mBluetoothAdapter.isEnabled()) {
    Toast.makeText(mContext, "BluetoothAdapter.isEnabled == false", Toast.LENGTH_LONG).show();
    return false;
}
// 獲取廣播者
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
    if (mBluetoothLeAdvertiser == null) {
    Toast.makeText(mContext, "BluetoothLeAdvertiser == null ", Toast.LENGTH_LONG).show();
    return false;
}

到這里如果 mBluetoothLeAdvertiser 為空,請(qǐng)換設(shè)備再繼續(xù)(國(guó)內(nèi)部分機(jī)型不支持)

建立服務(wù)

// 給個(gè)風(fēng)騷的廣播名稱(chēng),默認(rèn)是手機(jī)設(shè)置里藍(lán)牙的名稱(chēng)
mBluetoothAdapter.setName("Bleoo");
// 這個(gè)Callback 是設(shè)備廣播成功后所有狀態(tài)的回調(diào),包括讀寫(xiě)等
mGattServerCallback = new PeriServerCallBack();
// 打開(kāi)GattServer
mGattServer = mBluetoothManager.openGattServer(mContext, mGattServerCallback);
// 創(chuàng)建一個(gè)特征通道用來(lái)寫(xiě)
mWriteCharacter = new BluetoothGattCharacteristic(
        UUID.fromString(Constants.CHARACTERISTIC_WRITEABLE),
        BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_READ,
        BluetoothGattCharacteristic.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ);
// 創(chuàng)建一個(gè)特征通道用來(lái)讀
mReadCharacter = new BluetoothGattCharacteristic(
        UUID.fromString(Constants.CHARACTERISTIC_READABLE),
        BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_READ,
        BluetoothGattCharacteristic.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ);
// 創(chuàng)建一個(gè)Gatt服務(wù)
mGattService = new BluetoothGattService(
        UUID.fromString(Constants.GATT_SERVICE_PRIMARY),
        BluetoothGattService.SERVICE_TYPE_PRIMARY);
// 添加讀寫(xiě)通道
mGattService.addCharacteristic(mWriteCharacter);
mGattService.addCharacteristic(mReadCharacter);
// 添加服務(wù)
if (mGattServer != null && mGattService != null)
    mGattServer.addService(mGattService);

開(kāi)啟廣播

成功開(kāi)啟廣播后,中心設(shè)備能夠掃描到你的設(shè)備,當(dāng)然前提是中心設(shè)備支持ble設(shè)備,并且在掃描ble設(shè)備。
中心設(shè)備能獲取到你ble設(shè)備的所有信息,包括 GattService 及其 BluetoothGattCharacteristic。

public void startAdvertising() {
    // 這里的Callback是是否開(kāi)啟成功的回調(diào)
    mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(true, 0), createAdvertiseData(), mAdvCallback);
}
private AdvertiseSettings createAdvSettings(boolean connectable, int timeoutMillis) {
    AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
    //設(shè)置廣播的模式,跟功耗相關(guān)
    builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
    builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
    builder.setConnectable(connectable);
    builder.setTimeout(timeoutMillis);
    return builder.build();
}
//設(shè)置廣播數(shù)據(jù)(可以攜帶廣播數(shù)據(jù),這里沒(méi)有攜帶)
private AdvertiseData createAdvertiseData() {
    AdvertiseData.Builder builder = new AdvertiseData.Builder();
    builder.setIncludeDeviceName(true);
    return builder.build();
}

廣播回調(diào)

    //發(fā)送廣播的回調(diào)
    private AdvertiseCallback mAdvCallback = new AdvertiseCallback() {
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            mOnCallBackListener.advertisingStatus(true);
            if (settingsInEffect != null) {
                LogUtil.e("onStartSuccess TxPowerLv=" + settingsInEffect.getTxPowerLevel()
                        + " mode=" + settingsInEffect.getMode() + " timeout=" + settingsInEffect.getTimeout());
            } else {
                LogUtil.e("onStartSuccess, settingInEffect is null");
            }
        }

        public void onStartFailure(int errorCode) {
            mOnCallBackListener.advertisingStatus(false);
            LogUtil.e("onStartFailure errorCode=" + errorCode);
        }
    };

Gatt服務(wù)回調(diào)

private class PeriServerCallBack extends BluetoothGattServerCallback {

    //當(dāng)添加一個(gè)GattService成功后會(huì)回調(diào)改接口。
    @Override
    public void onServiceAdded(int status, BluetoothGattService service) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            LogUtil.e("onServiceAdded status=GATT_SUCCESS service=" + service.getUuid().toString());
        } else {
            LogUtil.e("onServiceAdded status!=GATT_SUCCESS");
        }
    }
    
    //BLE連接狀態(tài)改變后回調(diào)的接口
    @Override
    public void onConnectionStateChange(android.bluetooth.BluetoothDevice device, int status, int newState) {
        mClientDevice = device;
        LogUtil.e("BLE連接狀態(tài)改變 status=" + status + "->" + newState + " ==== Address: " + device.getAddress());
    }
    
    //當(dāng)有客戶(hù)端來(lái)讀數(shù)據(jù)時(shí)回調(diào)的接口
    @Override
    public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device,
                int requestId, int offset, BluetoothGattCharacteristic characteristic) {
        mClientDevice = device;
        LogUtil.e("客戶(hù)端讀數(shù)據(jù) requestId=" + requestId + " offset=" + offset);
        mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());
    }
    
    //當(dāng)有客戶(hù)端來(lái)寫(xiě)數(shù)據(jù)時(shí)回調(diào)的接口
    @Override
    public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
                BluetoothGattCharacteristic characteristic, boolean preparedWrite,
                boolean responseNeeded, int offset, byte[] value) {
        mClientDevice = device;
        try {
            String msg = new String(value, "UTF-8");
            mOnCallBackListener.writeRequest(msg);
            LogUtil.e("客戶(hù)端寫(xiě)數(shù)據(jù) + message= " + msg + " requestId= " + requestId + " offset= " + offset);
            } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // 必須sendResponse用于響應(yīng)(具體原因也不清楚,似乎是為了保持連接)
        mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
    }
    //當(dāng)有客戶(hù)端來(lái)寫(xiě)Descriptor時(shí)回調(diào)的接口
    @Override
    public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor,
                boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
        mClientDevice = device;
        LogUtil.e("onDescriptorWriteRequest === ");
        // now tell the connected device that this was all successfull
        mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
    }
}

寫(xiě)出數(shù)據(jù)

與中心設(shè)備的寫(xiě)出方法不同,周邊設(shè)備通過(guò) notifyCharacteristicChanged 方法,類(lèi)似通知的方法寫(xiě)出數(shù)據(jù)。
當(dāng)然,sendResponse 也能用于返回?cái)?shù)據(jù)。

    public boolean write(byte[] value) {
        if (mWriteCharacter == null)
            return false;
        if (mGattServer == null)
            return false;
        mWriteCharacter.setValue(value);
        return mGattServer.notifyCharacteristicChanged(mClientDevice, mWriteCharacter, false);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容