初识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));
 }
 }
 };
作者:炸憨啪原文地址:https://www.cnblogs.com/mhzf/p/17807550.html

%s 个评论

要回复文章请先登录注册