Wi-Fi 掃描總覽

您可以使用 WifiManager API 提供的 Wi-Fi 掃描功能,取得裝置可見的 Wi-Fi 存取點清單。

Wi-Fi 掃描程序

掃描程序分為三個步驟:

  1. SCAN_RESULTS_AVAILABLE_ACTION註冊廣播事件監聽器,在掃描要求完成時呼叫,提供成功/失敗狀態。對於搭載 Android 10 (API 級別 29) 以上版本的裝置,如果平台或其他應用程式在裝置上執行任何完整的 Wi-Fi 掃描作業,系統就會傳送這項廣播。應用程式可以使用廣播,被動地聆聽裝置上的所有掃描完成作業,而不需要自行發出掃描作業。

  2. 使用 WifiManager.startScan() 要求掃描。請務必檢查方法的傳回狀態,因為呼叫可能會因下列任一原因失敗:

    • 由於短時間內掃描次數過多,掃描要求可能會受到節流限制。
    • 裝置處於閒置狀態,且掃描功能已停用。
    • Wi-Fi 硬體回報掃描失敗。
  3. 使用 WifiManager.getScanResults() 取得掃描結果。系統傳回的掃描結果是最近更新的結果,如果目前的掃描作業尚未完成或成功,則可能來自先前的掃描作業。也就是說,如果您在收到成功的 SCAN_RESULTS_AVAILABLE_ACTION 廣播之前呼叫這個方法,可能會取得較舊的掃描結果。

以下程式碼提供實作這些步驟的範例:

Kotlin

val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager

val wifiScanReceiver = object : BroadcastReceiver() {

  override fun onReceive(context: Context, intent: Intent) {
    val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
    if (success) {
      scanSuccess()
    } else {
      scanFailure()
    }
  }
}

val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)

val success = wifiManager.startScan()
if (!success) {
  // scan failure handling
  scanFailure()
}

....

private fun scanSuccess() {
  val results = wifiManager.scanResults
  ... use new scan results ...
}

private fun scanFailure() {
  // handle failure: new scan did NOT succeed
  // consider using old scan results: these are the OLD results!
  val results = wifiManager.scanResults
  ... potentially use older scan results ...
}

Java

WifiManager wifiManager = (WifiManager)
                   context.getSystemService(Context.WIFI_SERVICE);

BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context c, Intent intent) {
    boolean success = intent.getBooleanExtra(
                       WifiManager.EXTRA_RESULTS_UPDATED, false);
    if (success) {
      scanSuccess();
    } else {
      // scan failure handling
      scanFailure();
    }
  }
};

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
context.registerReceiver(wifiScanReceiver, intentFilter);

boolean success = wifiManager.startScan();
if (!success) {
  // scan failure handling
  scanFailure();
}

....

private void scanSuccess() {
  List<ScanResult> results = wifiManager.getScanResults();
  ... use new scan results ...
}

private void scanFailure() {
  // handle failure: new scan did NOT succeed
  // consider using old scan results: these are the OLD results!
  List<ScanResult> results = wifiManager.getScanResults();
  ... potentially use older scan results ...
}

限制

Android 8.0 (API 級別 26) 推出了權限和 Wi-Fi 掃描頻率的限制。

為了改善網路效能、安全性和電池續航力,Android 9 (API 級別 28) 加強了權限要求,並進一步限制 Wi-Fi 掃描的頻率。

權限

Android 8.0 和 Android 8.1:

如要成功呼叫 WifiManager.getScanResults(),您必須具備下列任一權限:

如果呼叫應用程式不具備任何這些權限,呼叫就會失敗,並顯示 SecurityException

或者,在搭載 Android 8.0 (API 級別 26) 以上版本的裝置上,您可以使用 CompanionDeviceManager 代表應用程式掃描附近的隨附裝置,而不需要位置權限。如要進一步瞭解這個選項,請參閱「配件裝置配對」。

Android 9:

如要成功呼叫 WifiManager.startScan(),必須符合下列「所有」條件:

Android 10 (API 級別 29) 以上版本:

如要成功呼叫 WifiManager.startScan(),必須符合下列「所有」條件:

  • 如果應用程式指定的是 Android 10 (API 級別 29) SDK 以上版本,則應用程式具有 ACCESS_FINE_LOCATION 權限。
  • 如果應用程式指定的 SDK 版本低於 Android 10 (API 級別 29),則應用程式會具有 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 權限。
  • 您的應用程式具有 CHANGE_WIFI_STATE 權限。
  • 裝置上已啟用定位服務 (請前往「設定」 >「位置」)。

如要成功呼叫 WifiManager.getScanResults(),請確保滿足下列所有條件:

  • 如果應用程式指定 Android 10 (API 級別 29) SDK 以上版本,則會具備 ACCESS_FINE_LOCATION 權限。
  • 如果應用程式指定的 SDK 版本低於 Android 10 (API 級別 29),則應用程式會具備 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 權限。
  • 您的應用程式具有 ACCESS_WIFI_STATE 權限。
  • 裝置上已啟用定位服務 (請前往「設定」 >「位置」)。

如果呼叫應用程式未符合所有這些要求,通話就會失敗,並顯示 SecurityException

節流

以下限制適用於使用 WifiManager.startScan() 的掃描頻率。

Android 8.0 和 Android 8.1:

每個背景應用程式可在 30 分鐘內掃描一次。

Android 9:

每個前景應用程式可在 2 分鐘內掃描四次。這樣一來,您就能在短時間內執行大量掃描作業。

所有背景應用程式在 30 分鐘內可合併掃描一次。

Android 10 以上版本:

適用 Android 9 的相同節流限制。我們新增了一個開發人員選項,可切換節流功能,以便進行本機測試 (請依序前往「開發人員選項」>「網路」>「Wi-Fi 掃描節流」)。