「App is scanning too frequently"什麼鬼?

在前一篇文章中咱們分析了BluetoothLeScanner的源碼,瞭解了Android系統中關於低功耗藍牙掃描的業務邏輯。下面咱們就繼續分析關於低功耗藍牙掃描中的常見問題之一「App is scanning too frequently…」
bash

相信大多數開發郭低功耗藍牙的小夥伴應該都遇到過這個問題。這個問題會致使什麼?直接影響是咱們不能掃描到任何低功耗藍牙設備,這對於咱們開發者而言是很很差的。這不只影響用戶體驗,還讓你的領導很不理解,這種很尷尬的狀況,甚至會致使領導懷疑你的能力。那麼咱們應該怎樣解決這個問題呢?下面咱們就作一下具體分析。app

咱們在開發低功耗藍牙的過程當中,每每會遇到連續使用低功耗藍牙的掃描(bluetoothLeScanner.startScan())幾回後就會出現,掃描不到任何設備的狀況。咱們查看日誌發現:當出現這種問題時,通常日誌會出現2019-11-22 15:58:00.281 2216-3350/? E/BtGatt.GattService: App '***' is scanning too frequently這樣的信息。估計好多人也像我同樣掃描太頻繁了??還有這操做,天哪這怎麼整?若是小夥伴看了我上一篇文章了,就會發如今BluetoothLeScanner中的BleScanCallbackWrapper有一個startRegistration()的方法,以下圖:ide

若是掃描太過頻繁就會致使掃描失敗,可是並不會將事件反饋至App。頓時感受好大的坑啊。那麼咱們不由會產生疑問:什麼標準能夠認定爲太過頻繁?這是怎麼界定的呢?咱們又應該怎麼應對?帶着這些疑問,咱們須要閱讀源碼了。。。ui

可是咱們仔細觀察剛纔的異常日誌,咱們發現有個「GattService」。咱們使用Android Studio查看源碼,發現並無該文件,那麼咱們只能經過系統的源碼找到答案了~路漫漫其修遠兮。this

經過系統源碼咱們發現GattService是一個服務,用於進行Gatt相關操做,仍是hide的,難怪找不到啊。咱們在GattService類中找到關於開始掃描的相關方法,代碼以下:spa

void startScan(int appIf, boolean isServer, ScanSettings settings,
            List<ScanFilter> filters, WorkSource workSource,
            List<List<ResultStorageDescriptor>> storages, String callingPackage) {
        if (DBG) Log.d(TAG, "start scan with filters");
		//省略部分代碼...
       
        AppScanStats app = null;
        if (isServer) {
            app = mServerMap.getAppScanStatsById(appIf);
        } else {
            app = mClientMap.getAppScanStatsById(appIf);
        }

        if (app != null) {
            if (app.isScanningTooFrequently() &&
                checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED) != PERMISSION_GRANTED) {
                Log.e(TAG, "App '" + app.appName + "' is scanning too frequently");
                return;
            }
            scanClient.stats = app;
            app.recordScanStart(settings);
        }

        mScanManager.startScan(scanClient);
    }複製代碼

在該方法裏面存在一個檢查是否掃描太頻繁的方法,咱們接下來就須要找到AppScanStats 這個文件。咱們查看AppScanStats源碼,發現該方法:.net

synchronized boolean isScanningTooFrequently() {
        if (lastScans.size() < NUM_SCAN_DURATIONS_KEPT) {
            return false;
        }

        return (System.currentTimeMillis() - lastScans.get(0).timestamp) <
            EXCESSIVE_SCANNING_PERIOD_MS;
    }複製代碼

這裏這兩個靜態變量分別是: NUM_SCAN_DURATIONS_KEPT和EXCESSIVE_SCANNING_PERIOD_MS日誌

從字面意思咱們也能猜得出來,下面咱們從代碼中找答案。code

static final int NUM_SCAN_DURATIONS_KEPT = 5;

// This constant defines the time window an app can scan multiple times.
// Any single app can scan up to |NUM_SCAN_DURATIONS_KEPT| times during
// this window. Once they reach this limit, they must wait until their
// earliest recorded scan exits this window.
static final long EXCESSIVE_SCANNING_PERIOD_MS = 30 * 1000;複製代碼

EXCESSIVE_SCANNING_PERIOD_MS變量定義了一個app掃描的時間窗口,任何App進行低功耗藍牙掃描達到5次,一旦到達這個限制,必須等待最先的那次掃描結束才能從新開始。這個時間限制就是30s.這就是答案。固然解決方法也挺簡單,只須要保證5次掃描總時長超過30s就能解決該問題cdn

相關文章
相關標籤/搜索