首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Android 驗證指定 WiFi 真正連接成功:廣播監聽 NETWORK_STATE_CHANGED_ACTION

     編輯:程式設計疑難解答

問題背景

調用 `WifiManager.addNetwork()` 返回 `true` 只表示「配置下發成功」,並不代表 WiFi 實際已關聯、獲取 IP、可通訊。直接比對 SSID 並在網路請求時仍會失敗。

正確驗證流程:監聽系統廣播

Android 系統在 WiFi 連接狀態真正變化時,會發送有序廣播:

  • WifiManager.NETWORK_STATE_CHANGED_ACTION:標準、推薦,攜帶 `NetworkInfo` 詳情。
  • WifiManager.SUPPLICANT_STATE_CHANGED_ACTION:較底層,Supplicant 狀態機變化(ASSOCIATING、ASSOCIATED、COMPLETED、DISCONNECTED)。

完整實現範例

public class WifiConnectVerifier {
    private final Context context;
    private final String targetSSID; // 目標 SSID(含雙引號,如 "\"MyWiFi\"")
    private final VerificationCallback callback;
    private BroadcastReceiver receiver;

    public interface VerificationCallback {
        void onConnected();   // 真正連上、有 IP
        void onFailed(int reason); // 連接失敗/超時
    }

    public WifiConnectVerifier(Context context, String targetSSID, VerificationCallback callback) {
        this.context = context;
        this.targetSSID = targetSSID;
        this.callback = callback;
    }

    public void startListening() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        // 可選:同時監聽 SUPPLICANT_STATE_CHANGED_ACTION 獲取更細粒度狀態

        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context ctx, Intent intent) {
                if (!WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) return;

                NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                if (info == null) return;

                // 只關心目標 SSID
                WifiManager wm = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
                WifiInfo current = wm.getConnectionInfo();
                if (current == null || !targetSSID.equals(current.getSSID())) return;

                switch (info.getDetailedState()) {
                    case CONNECTED:
                        // 關鍵:再次確認有 IP 地址
                        if (info.isConnected() && current.getIpAddress() != 0) {
                            stopListening();
                            callback.onConnected();
                        }
                        break;
                    case DISCONNECTED:
                    case FAILED:
                    case BLOCKED:
                        stopListening();
                        callback.onFailed(info.getDetailedState().ordinal());
                        break;
                    default:
                        // OBTAINING_IPADDR、VERIFYING_POOR_LINK 等中間態,繼續等待
                        break;
                }
            }
        };
        context.registerReceiver(receiver, filter);
    }

    public void stopListening() {
        if (receiver != null) {
            try { context.unregisterReceiver(receiver); } catch (IllegalArgumentException ignored) {}
            receiver = null;
        }
    }
}

使用方式

WifiManager wm = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
WifiConfiguration conf = new WifiConfiguration();
conf.SSID = "\"TargetSSID\"";
conf.preSharedKey = "\"password\"";
int netId = wm.addNetwork(conf);
wm.enableNetwork(netId, true); // 觸發連接

new WifiConnectVerifier(this, "\"TargetSSID\"", new WifiConnectVerifier.VerificationCallback() {
    @Override public void onConnected() {
        runOnUiThread(() -> Toast.makeText(MainActivity.this, "WiFi 真正連接成功", Toast.LENGTH_SHORT).show());
        // 此處發起網路請求安全可靠
    }
    @Override public void onFailed(int reason) {
        Log.w("WiFi", "連接失敗,狀態碼: " + reason);
    }
}).startListening();

// 記得在 onDestroy() 呼叫 stopListening() 避免洩漏

關鍵點總結

誤區正確做法
信任 `addNetwork`/`enableNetwork` 返回值只表示配置下發,不代表關聯成功
輪詢 `WifiInfo.getSSID()` 比對競態條件、無法感知 IP 獲取完成
只檢查 `NetworkInfo.State.CONNECTED`需結合 `DetailedState.CONNECTED` + `getIpAddress() != 0`
註冊廣播用 Application Context✅ 正確,避免 Activity 銷毀導致洩漏/崩潰

Android 10+ (API 29+) 注意事項

  • 非系統應用無法直接啟用/停用網絡,需使用 `WifiNetworkSpecifier` + `ConnectivityManager.requestNetwork()`。
  • SSID 獲取需 `ACCESS_FINE_LOCATION` 權限,且 `getSSID()` 可能返回 ``,建議用 `WifiInfo.getNetworkId()` 配合 `WifiManager.getConfiguredNetworks()` 反查。
  • 廣播 `NETWORK_STATE_CHANGED_ACTION` 依然有效,但部分狀態變化改為 `ConnectivityManager.NetworkCallback`。

參考資源

詳細博客:Android WiFi 連接狀態驗證最佳實踐

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved