1. 程式人生 > >藍芽開發(二)掃描裝置

藍芽開發(二)掃描裝置

一、申請位置許可權

在Android6.0以後要掃描藍芽裝置,還需要請求位置許可權:

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

位置許可權屬於危險許可權,因此需要動態獲取:

//判斷是否有許可權
if (ContextCompat.checkSelfPermission(this,
        Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//請求許可權
      ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
    //判斷是否需要 向用戶解釋,為什麼要申請該許可權
    if(ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.READ_CONTACTS)) {
                Toast.makeText(this,"shouldShowRequestPermissionRationale",
Toast.LENGTH_SHORT).show();
    }
}
//許可權申請結果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]
      grantResults) {
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

在實際開發的過程中你也可以設定只有支援藍芽的裝置才能執行該APP:

<uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />

這裡設定required為true就是隻有在支援藍芽的裝置上才能執行,但是如果想讓你的app提供給那些不支援BLE的裝置,需要在manifest中包括上面程式碼並設定required="false",然後在執行時可以通過使用PackageManager.hasSystemFeature()確定BLE的可用性。

// 使用此檢查確定BLE是否支援在裝置上,然後你可以有選擇性禁用BLE相關的功能
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

二、掃描方式

Android官方提供的藍芽掃描方式有三種,分別是:

  1. BluetoothAdapter.startDiscovery()//可以掃描經典藍芽和ble藍芽兩種
  2. BluetoothAdapter.startLeScan()//掃描低功耗藍芽,在api21已經棄用,不過還是可以使用
  3. BluetoothLeScanner.startScan()//新的ble掃描方法

startDiscovery()方法在大多數手機上是可以同時發現經典藍芽和低功耗藍芽(BLE)的,但是startDiscovery()的回撥無法返回BLE的廣播,所以無法通過廣播識別裝置,而且startDiscovery()掃描BLE效率比startLeScan()低很多。因此需要根據具體的需求去做適配,才能更高效的搜尋藍芽。PS: startLeScan()和startScan()有過載方法可以指定規則,引數去搜索。

市面上的藍芽裝置,現在一般都是低功耗的藍芽裝置,因此只需要用下面兩種掃描方式就OK了

2.1.startLeScan方式

官方提供的事例程式碼正是使用了這種方式,startLeScan方法有兩個:

public boolean startLeScan(LeScanCallback callback)
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback)

檢視原始碼會發現一個引數的方法是呼叫了兩個引數的方法,只是serviceuuid[]傳入為null,也就是過濾條件為空。這裡我使用的是沒有過濾條件的方法:

// Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    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);
        }
        ...
    }

// Device scan callback.
    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            //掃描到的裝置資訊,將字元陣列轉成16進位制字串
            String scanRecordStr = SysConvert.bytesToHex(scanRecord);
            //在這裡可以做一下簡單的操作,避免多操作造成效能問題
        }
    };

因為在掃描藍芽是一個非常非常耗能的操作,因此一定要掃描一段時間就關閉,如果需要迴圈掃描,可以通過定時器實現掃描的開關操作

注意:開啟和關閉所使用的回撥必須是同一個,雖然關閉一個沒有開啟的回撥沒有錯誤返回。

2.2.startScan方式

我在專案中是使用startScan()掃描的,因為可以通過設定一些引數來靈活控制掃描的頻率等。

/**
     * 掃描裝置
     */
    public void scanDevices() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mProgressDialog.dismiss();
                if (mBluetoothLeScanner != null) {
                    mBluetoothLeScanner.stopScan(scanCallback);
                }
            }
        },  1000);
        mBluetoothLeScanner.startScan(null, createScanSetting(), scanCallback);
    } 
/**
     * 掃描廣播資料設定
     *
     * @return
     */
    public ScanSettings createScanSetting() {
        ScanSettings.Builder builder = new ScanSettings.Builder();
        builder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
        //builder.setReportDelay(100);//設定延遲返回時間
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
        }
        return builder.build();
    }
/**
* 回撥
*/
private ScanCallback scanCallback=new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            byte[] scanData=result.getScanRecord().getBytes();
            //把byte陣列轉成16進位制字串,方便檢視

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

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

ScanSettings :

setScanMode:有三種模式分別是

SCAN_MODE_LOW_POWER--------耗電最少,掃描時間間隔最短

SCAN_MODE_BALANCED---------平衡模式,耗電適中,掃描時間間隔一般,我使用這種模式來更新裝置狀態

SCAN_MODE_LOW_LATENCY---------最耗電,掃描延遲時間短,開啟掃描需要立馬返回結果可以使用

setReportDelay:設定掃描返回延遲時間,一般是大於零的毫秒值

如果設定了該屬性,掃描結果會以list形式在onBatchScanResults方法返回,在onScanResult中沒有返回,如果不設定則在onScanResult中返回,返回的資訊包含在result中,可以通過getBytes返回每一個裝置資訊的字元陣列,getDevice獲取BluetoothDevice裝置,在通過字元陣列和藍芽裝置物件獲取你想要的資訊

本人也是一個藍芽開發新手,如果上面有什麼地方有問題,請指出,共同探討,共同進步,不勝感激!