最近负责的一个简单定制化的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)); } } };