最近负责的一个简单定制化的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 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 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, 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 newResults) {        synchronized (mLock) {            evictOldScans();            for (ScanResult result : newResults) {                final Pair 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 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 getScanResults(long maxScanAgeMillis) throws IllegalArgumentException {        if (maxScanAgeMillis > mMaxScanAgeMillis) {            throw new IllegalArgumentException(                    "maxScanAgeMillis argument cannot be greater than mMaxScanAgeMillis!");        }        synchronized (mLock) {            List ageFilteredResults = new ArrayList();            for (ScanResult result : mScanResultsBySsidAndBssid.values()) {                if (mClock.millis() - result.timestamp / 1000                     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_ACTION2、扫描结果  SCAN_RESULTS_AVAILABLE_ACTION3、Wi-Fi 信号强度变化  RSSI_CHANGED_ACTION4、配置的网络发生变化 CONFIGURED_NETWORKS_CHANGED_ACTION5、网络状态改变  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));            }        }    };