初识Setting 应用WIFI设置

最近负责的一个简单定制化的setting,需要学习Wifi这一块方面的内容。通过这篇文章来了解一下原生的Setting 处理Wifi 的方式。有错误也希望大家提出来,我改进!

使用步骤

  • 申请权限、获取系统服务 WifiManager。
  • 通过 wifiManager.startScan(); 扫描WiFi 列表 。注意这个动作是耗时的
  • 注册广播获取wifi扫描结果

简单用法

示例代码

下面是Android Studio Bito 生成的一个简单的示例代码,展示如何搜索Wi-Fi并使用列表展示。

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.List;
 public class MainActivity extends AppCompatActivity {
    private static final int PERMISSIONS_REQUEST_CODE = 100;
    private WifiManager wifiManager;
    private List<ScanResult> wifiList;
    private ListView listView;
    private Button scanButton;
    private WifiScanReceiver wifiScanReceiver;
     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         listView = findViewById(R.id.listView);
        scanButton = findViewById(R.id.scanButton);
         wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
         scanButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                scanWifi();
            }
        });
         wifiScanReceiver = new WifiScanReceiver();
    }
     private void scanWifi() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_REQUEST_CODE);
        } else {
            performWifiScan();
        }
    }
     private void performWifiScan() {
        registerReceiver(wifiScanReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
        wifiManager.startScan();
    }
     private class WifiScanReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                wifiList = wifiManager.getScanResults();
                if (wifiList != null) {
                    List<String> wifiNames = new ArrayList<>();
                    for (ScanResult scanResult : wifiList) {
                        wifiNames.add(scanResult.SSID);
                    }
                    WifiListAdapter adapter = new WifiListAdapter(MainActivity.this, wifiNames);
                    listView.setAdapter(adapter);
                } else {
                    Toast.makeText(MainActivity.this, "No Wi-Fi networks found", Toast.LENGTH_SHORT).show();
                }
            }
            unregisterReceiver(this);
        }
    }
     @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == PERMISSIONS_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                performWifiScan();
            } else {
                Toast.makeText(this, "Permission denied. Unable to scan Wi-Fi networks.", Toast.LENGTH_SHORT).show();
            }
        }
    }
     @Override
    protected void onDestroy() {
        super.onDestroy();
        if (wifiScanReceiver != null) {
            unregisterReceiver(wifiScanReceiver);
        }
    }
}

ScanResult

是Android中的一个类,用于表示Wi-Fi扫描的结果。当您使用Wi-Fi功能进行扫描时,将返回一个ScanResult对象的列表,每个对象表示一个扫描到的Wi-Fi网络。ScanResult类提供了一些有用的信息,可以帮助您获取和分析附近的Wi-Fi网络。以下是一些ScanResult类提供的常用信息:

  • SSID:表示Wi-Fi网络的名称。
  • BSSID:表示Wi-Fi网络的MAC地址。
  • level:表示Wi-Fi信号的强度,以dBm为单位。
  • frequency:表示Wi-Fi信号的频率。
  • capabilities:表示Wi-Fi网络的安全和认证功能。
  • timestamp:表示扫描结果的时间戳。
  • channelWidth:表示Wi-Fi信号的通道宽度。
  • centerFreq0和centerFreq1:表示Wi-Fi信号的中心频率。
  • is80211mcResponder:表示Wi-Fi网络是否支持802.11mc(Wi-Fi Round-Trip Time)协议。
    等等。

Setting 源码分析

平常可以在这里看Android 源码 http://aospxref.com/ (偶尔可能访问不了)
这里是Android 13 http://aospxref.com/android-13.0.0_r3/xref/packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java 不同的系统版本可能不一样,应该是大差不差的。

红色圈住的部分比较重要。WifiPickerTracker 是什么,接着走进去发现又继承了BaseWifiTracker。可以直接看这个基类。

BaseWifiTracker

构造方法

传参注册生命周期

/**
     * Constructor for BaseWifiTracker.
     * @param wifiTrackerInjector injector for commonly referenced objects.
     * @param lifecycle Lifecycle this is tied to for lifecycle callbacks.
     * @param context Context for registering broadcast receiver and for resource strings.
     * @param wifiManager Provides all Wi-Fi info.
     * @param connectivityManager Provides network info.
     * @param mainHandler Handler for processing listener callbacks.
     * @param workerHandler Handler for processing all broadcasts and running the Scanner.
     * @param clock Clock used for evaluating the age of scans
     * @param maxScanAgeMillis Max age for tracked WifiEntries.
     * @param scanIntervalMillis Interval between initiating scans.
     */
    BaseWifiTracker(
            @NonNull WifiTrackerInjector injector,
            @NonNull Lifecycle lifecycle, @NonNull Context context,
            @NonNull WifiManager wifiManager,
            @NonNull ConnectivityManager connectivityManager,
            @NonNull Handler mainHandler,
            @NonNull Handler workerHandler,
            @NonNull Clock clock,
            long maxScanAgeMillis,
            long scanIntervalMillis,
            BaseWifiTrackerCallback listener,
            StLifecyclering tag) {
        mInjector = injector;
        lifecycle.addObserver(this);   //这里注册了生命周期Lifecycle
        mContext = context;
        mWifiManager = wifiManager;
        mConnectivityManager = connectivityManager;
        mMainHandler = mainHandler;     //主线程处理监听器回调。
        mWorkerHandler = workerHandler;  //处理所有广播和运行扫描器的处理程序。
        mMaxScanAgeMillis = maxScanAgeMillis;  //设定ScanResult的最大存活时间
        mScanIntervalMillis = scanIntervalMillis; //启动WifiPickerTracker扫描的间隔时间
        mListener = listener;
        mTag = tag;

        mScanResultUpdater = new ScanResultUpdater(clock,
                maxScanAgeMillis + scanIntervalMillis);  
        mScanner = new BaseWifiTracker.Scanner(workerHandler.getLooper());
        sVerboseLogging = mWifiManager.isVerboseLoggingEnabled();
        updateDefaultRouteInfo();
    }

Scanner

扫描WiFi动作

@WorkerThread  
private class Scanner extends Handler {  
    private static final int SCAN_RETRY_TIMES = 3;  //扫描WiFi 最大失败次数
  
    private int mRetry = 0;  
    private boolean mIsActive;  
  
    private Scanner(Looper looper) {  
        super(looper);  
    }  
  
    private void start() {  
        Log.d("TAG", "start: 疑问不知道哪里发的广播????");
        if (!mIsActive) {  
            mIsActive = true;  
            if (isVerboseLoggingEnabled()) {  
                Log.v(mTag, "Scanner start");  
            }  
            postScan();  
        }  
    }  
  
    private void stop() {  
        mIsActive = false;  
        if (isVerboseLoggingEnabled()) {  
            Log.v(mTag, "Scanner stop");  
        }  
        mRetry = 0;  
        removeCallbacksAndMessages(null);  
    }  

    //一直重复扫描WiFi设备。
    private void postScan() {  
        if (mWifiManager.startScan()) {  
            mRetry = 0;  
        } else if (++mRetry >= SCAN_RETRY_TIMES) {  //失败3次跳出循环
            // TODO(b/70983952): See if toast is needed here  
            if (isVerboseLoggingEnabled()) {  
                Log.v(mTag, "Scanner failed to start scan " + mRetry + " times!");  
            }  
            mRetry = 0;  
            return;       
         }  
        postDelayed(this::postScan, mScanIntervalMillis);  
    }  
}

ScanResultUpdater

主要更新扫描结果

package com.android.wifitrackerlib;

import android.net.wifi.ScanResult;
import android.util.Pair;

import androidx.annotation.NonNull;

import java.time.Clock;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Utility class to keep a running list of scan results merged by SSID+BSSID pair.
 *
 * Thread-safe.
 */
public class ScanResultUpdater {
    private Map<Pair<String, String>, ScanResult> mScanResultsBySsidAndBssid = new HashMap<>();
    private final long mMaxScanAgeMillis;
    private final Object mLock = new Object();
    private final Clock mClock;

    /**
     * Creates a ScanResultUpdater with no max scan age.
     *
     * @param clock Elapsed real time Clock to compare with ScanResult timestamps.
     */
    public ScanResultUpdater(Clock clock) {
        this(clock, Long.MAX_VALUE);
    }

    /**
     * Creates a ScanResultUpdater with a max scan age in milliseconds. Scans older than this limit
     * will be pruned upon update/retrieval to keep the size of the scan list down.
     */
    public ScanResultUpdater(Clock clock, long maxScanAgeMillis) {
        mMaxScanAgeMillis = maxScanAgeMillis;
        mClock = clock;
    }

    /**
     * Updates scan result list and replaces older scans of the same SSID+BSSID pair.
     * 更新扫描结果列表并替换相同SSID+BSSID对的旧扫描。
     */
    public void update(@NonNull List<ScanResult> newResults) {
        synchronized (mLock) {
            evictOldScans();

            for (ScanResult result : newResults) {
                final Pair<String, String> key = new Pair(result.SSID, result.BSSID);
                ScanResult prevResult = mScanResultsBySsidAndBssid.get(key);
                if (prevResult == null || (prevResult.timestamp < result.timestamp)) {
                    mScanResultsBySsidAndBssid.put(key, result);
                }
            }
        }
    }

    /**
     * Returns all seen scan results merged by SSID+BSSID pair.
     */
    @NonNull
    public List<ScanResult> getScanResults() {
        return getScanResults(mMaxScanAgeMillis);
    }

    /**
     * Returns all seen scan results merged by SSID+BSSID pair and newer than maxScanAgeMillis.
     * maxScanAgeMillis must be less than or equal to the mMaxScanAgeMillis field if it was set.
     */
    @NonNull
    public List<ScanResult> getScanResults(long maxScanAgeMillis) throws IllegalArgumentException {
        if (maxScanAgeMillis > mMaxScanAgeMillis) {
            throw new IllegalArgumentException(
                    "maxScanAgeMillis argument cannot be greater than mMaxScanAgeMillis!");
        }
        synchronized (mLock) {
            List<ScanResult> ageFilteredResults = new ArrayList<>();
            for (ScanResult result : mScanResultsBySsidAndBssid.values()) {
                if (mClock.millis() - result.timestamp / 1000 <= maxScanAgeMillis) {
                    ageFilteredResults.add(result);
                }
            }
            return ageFilteredResults;
        }
    }

    //清除旧的扫描结果 
    //即扫描结果的时间戳距离当前时间超过了设定的最大扫描时间,那么该扫描结果会被移除
    private void evictOldScans() {
        synchronized (mLock) {
            mScanResultsBySsidAndBssid.entrySet().removeIf((entry) ->
                    mClock.millis() - entry.getValue().timestamp / 1000 > mMaxScanAgeMillis);
        }
    }
}

生命周期

  • Lifecycle.Event.ON_START 在onStart 中注册广播接收器处理网络回调
  • Lifecycle.Event.ON_STOP 取消广播接收器的注册,网络回调,并暂停扫描机制。
/**
     * Registers the broadcast receiver and network callbacks and starts the scanning     mechanism.
     * 注册广播接收器和网络回调,并启动扫描机制。
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    @MainThread
    public void onStart() {
        mWorkerHandler.post(() -> {
            updateDefaultRouteInfo();
            IntentFilter filter = new IntentFilter();
            filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
            filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
            filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
            filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
            filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
            filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
            mContext.registerReceiver(mBroadcastReceiver, filter,
                    /* broadcastPermission */ null, mWorkerHandler);
            mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback,
                    mWorkerHandler);
            NonSdkApiWrapper.registerSystemDefaultNetworkCallback(
                    mConnectivityManager, mDefaultNetworkCallback, mWorkerHandler);
            handleOnStart();
            mIsInitialized = true;
        });
    }

    /**
     * Unregisters the broadcast receiver, network callbacks, and pauses the scanning mechanism.
     * 取消广播接收器的注册,网络回调,并暂停扫描机制。
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    @MainThread
    public void onStop() {
        mWorkerHandler.post(() -> {
            mScanner.stop();
            mContext.unregisterReceiver(mBroadcastReceiver);
            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
            mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback);
        });
    }



/**
*广播接收器处理网络回调
1、WiFi状态改变  WIFI_STATE_CHANGED_ACTION
2、扫描结果  SCAN_RESULTS_AVAILABLE_ACTION
3、Wi-Fi 信号强度变化  RSSI_CHANGED_ACTION
4、配置的网络发生变化 CONFIGURED_NETWORKS_CHANGED_ACTION
5、网络状态改变  NETWORK_STATE_CHANGED_ACTION
*/
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        @WorkerThread
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (isVerboseLoggingEnabled()) {
                Log.v(mTag, "Received broadcast: " + action);
            }

            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
                mWifiState = intent.getIntExtra(
                        WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
                if (mWifiState == WifiManager.WIFI_STATE_ENABLED) {
                    mScanner.start();
                } else {
                    mScanner.stop();
                }
                notifyOnWifiStateChanged();
                handleWifiStateChangedAction();
            } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
                handleScanResultsAvailableAction(intent);
            } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)) {
                handleConfiguredNetworksChangedAction(intent);
            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
                handleNetworkStateChangedAction(intent);
            } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
                handleRssiChangedAction();
            } else if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
                handleDefaultSubscriptionChanged(intent.getIntExtra(
                        "subscription", SubscriptionManager.INVALID_SUBSCRIPTION_ID));
            }
        }
    };