Android5.0(Lollipop) BLE藍牙4.0+淺析code(二)


作者:Bgwan
鏈接:https://zhuanlan.zhihu.com/p/23347612
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

Android5.0(Lollipop) BLE藍牙4.0+淺析code說明(二)

Android4.4 啟用藍牙相關的操作方法主要是,BluetoothAdapter.startleScan();方法,最新的Android 5.0已經棄用了改方法,谷歌官方已經改用了android.bluetooth.le類來處理BLE的操作,所以我建議還是用最新的接口開發。但是為了兼容以前的版本改方法還是可以使用,上篇日志已經提到我們可以加一個SDK版本的判斷,這里不過多說明,

這次更新總共在le下面添加了6個scan的相關類(4個advertise相關類),下面開始介紹這個類的作用。

1.BLE centrial的代碼

mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

利用adapter來得到一個scanner對象,這句代碼和Peripheral的getBluetoothLeAdvertiser基本類似,一般手機支持BLE都會支持Central,除非是本身就只是外設設備。

advertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();

如果是Peripheral 得到advertiser對象,那么我們可以廣播設置一些參數,數據,加一個CallBack,如下:

advertiser.startAdvertising(settings, data, mAdvertiseCallback);

這里面的三個參數,不再本文導論范圍,但是可以看一下

// TODO Auto-generated method stub
AdvertiseSettings settings = new AdvertiseSettings.Builder()
        .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
        .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
        .setConnectable(false).build();
ParcelUuid pUuid = new ParcelUuid(UUID.fromString("00001000-0000-1000-8000-00805f9b34fb"));
AdvertiseData data = new AdvertiseData.Builder()
        .setIncludeDeviceName(true)
        .addServiceUuid(pUuid)
        .addServiceData(pUuid,
"Data".getBytes(Charset.forName("UTF-8"))).build();
final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
if (settingsInEffect != null) {
            Log.d(TAG, "onStartSuccess TxPowerLv=" + settingsInEffect.getTxPowerLevel() + " mode=" + settingsInEffect.getMode()
                    + " timeout=" + settingsInEffect.getTimeout());
        } else {
            Log.e(TAG, "onStartSuccess, settingInEffect is null");
        }
        Log.e(TAG, "onStartSuccess settingsInEffect" + settingsInEffect);

    }

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

if (errorCode == ADVERTISE_FAILED_DATA_TOO_LARGE) {
            Toast.makeText(mContext, "Operation failed due to an internal error", Toast.LENGTH_LONG).show();
            Log.e(TAG, "Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes.");
        } else if (errorCode == ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
            Toast.makeText(mContext, "Operation failed due to an internal error", Toast.LENGTH_LONG).show();
            Log.e(TAG, "Failed to start advertising because no advertising instance is available.");
        } else if (errorCode == ADVERTISE_FAILED_ALREADY_STARTED) {
            Toast.makeText(mContext, "Operation failed due to an internal error", Toast.LENGTH_LONG).show();
            Log.e(TAG, "Failed to start advertising as the advertising is already started");
        } else if (errorCode == ADVERTISE_FAILED_INTERNAL_ERROR) {
            Toast.makeText(mContext, "Operation failed due to an internal error", Toast.LENGTH_LONG).show();
            Log.e(TAG, "Operation failed due to an internal error");
        } else if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
            Toast.makeText(mContext, "This feature is not supported on this platform", Toast.LENGTH_LONG).show();
            Log.e(TAG, "This feature is not supported on this platform");
        } else {
            Toast.makeText(mContext, "onStartFailure errorCode", Toast.LENGTH_LONG).show();
            Log.e(TAG, "onStartFailure errorCode" + errorCode);
        }
    }
};

它主要的作用就是,假如說你有一個嵌入式的外設,藍牙設備名字CTH-1001,這個藍牙設備你可以通過設置這些參數去過濾你想要的藍牙設備,如果周圍還有其他的藍牙外設你的手機可能就搜索不到,這也是在Android 后來新加入的功能,為了方便開發者使用。

2,接下來就是Scan的動作了,Scan新的接口動作把Scan分為了兩類,

第一類:直接給一個callback

public void startScan(java.util.List<android.bluetooth.le.ScanFilter> filters, android.bluetooth.le.ScanSettings settings, android.bluetooth.le.ScanCallback callback) 
/**
 * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
 * delivered through {@code callback}.
 * <p>
 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
 *
 * @param callback Callback used to deliver scan results.
 * @throws IllegalArgumentException If {@code callback} is null.
 */
public void startScan(final ScanCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback is null");
    }
    startScan(null, new ScanSettings.Builder().build(), callback);
}

直接搜索全部周圍peripheral設備,當然這里你要填寫callback,

第二類為這里加入了新的過濾條件。可以看到ScanFilter:

    /**
    * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
    * <p>
* Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @param filters {@link ScanFilter}s for finding exact BLE devices.
* @param settings Settings for the scan.
* @param callback Callback used to deliver scan results.
* @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
*/
public void startScan(List<ScanFilter> filters, ScanSettings settings,
    final ScanCallback callback) {
    startScan(filters, settings, callback, null);
    }

這一種明顯屬於定制化的函數了,因為他需要我們輸入過濾條件。這里的ScanFilter和ScanSettings又是兩個scan類,當然這兩個類的目的主要是為了有些人想單獨為某個產品開發應用,把過濾條件加上,比如DeviceName或者某個Service UUID等等,就可以搜索出只針對特定Peripheral特性的設備,就像我們公司的CTH-1001類型的設備。

單獨看這兩個新的接口,可能有些人會迷惑,本來不久應該是這樣嗎?其實Andoid L之前,scan接口不是這樣的,就只有上面兩種中的一種方法,suo'y。

3,OKay ,講解了scanLeDevice,這里看一下回調mScanCallBack具體怎么實現的,其實在上篇文章已經貼出來了,

private ScanCallback mScanCallback = new ScanCallback() {
@Override
        public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (result == null || result.getDevice() == null
                    || TextUtils.isEmpty(result.getDevice().getName())) {
mText.setText("沒有搜索到藍牙設備");
//                return;
            }
//如果是外設則可能存在沒有ServiceUuids
//            builder.append("/n").append(
//                    new String(result.getScanRecord().getServiceData(
//                            result.getScanRecord().getServiceUuids().get(0)),
//                            Charset.forName("UTF-8")));

            BluetoothDevice device = result.getDevice();
            Log.d(TAG, "Device name: " + device.getName());
            Log.d(TAG, "Device address: " + device.getAddress());
            Log.d(TAG, "Device service UUIDs: " + device.getUuids());
if (builder.toString().contains(device.getName())) {
            } else {
builder.append("\n" + device.getName() + "&" + device.getAddress() + "\n");
            }
            ScanRecord record = result.getScanRecord();
            Log.d(TAG, "Record advertise flags: 0x" + Integer.toHexString(record.getAdvertiseFlags()));
            Log.d(TAG, "Record Tx power level: " + record.getTxPowerLevel());
            Log.d(TAG, "Record device name: " + record.getDeviceName());
            Log.d(TAG, "Record service UUIDs: " + record.getServiceUuids());
            Log.d(TAG, "Record service data: " + record.getServiceData());

mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();

mText.setText("搜索結果,builder:" + builder.toString());
        }

看到沒有,這里包含result就是onScanResult(int callbackType, ScanResult result)的返回值,而且對於如何解析的代碼我也貼出來了,是不是應該感動一下。

最后,所有的操作完了,不要忘記stop一下,我們可以在OnDestory方法中停止,當然加一個postdelay,這里操作我寫了一個方法直接在需要的地方調用。

mHandler.postDelayed(new Runnable() {
@Override
    public void run() {
        stopScanning();
    }
}, DEFAULT_SCAN_PERIOD);

停止方法,

 private void stopScanning() {
if (mBluetoothLeScanner != null) {
        Log.d(TAG, "Stop scanning.");
mBluetoothLeScanner.stopScan(mBleScanCallback);
    }
}

剩下的就是connectGatt了,下篇文章我會重點介紹,藍牙之間如何連接,如果傳遞數據,可能會結合我們公司的項目來講解,

對了,我猜大家肯定需要源碼,等公司項目完了,就上傳一個修改后的demo給大家,歡迎大家關注。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM