初识Setting 应用WIFI设置

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

使用步骤

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

简单用法

示例代码

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

java 复制代码
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 源码 aospxref.com/ (偶尔可能访问不了) 这里是Android 13 aospxref.com/android-13.... 不同的系统版本可能不一样,应该是大差不差的。

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

BaseWifiTracker

构造方法

传参注册生命周期

java 复制代码
/**
     * 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动作

java 复制代码
@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

主要更新扫描结果

java 复制代码
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 取消广播接收器的注册,网络回调,并暂停扫描机制。
java 复制代码
/**
     * 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));
            }
        }
    };
相关推荐
zhangphil35 分钟前
Android ValueAnimator ImageView animate() rotation,Kotlin
android·kotlin
徊忆羽菲1 小时前
CentOS7使用源码安装PHP8教程整理
android
编程、小哥哥2 小时前
python操作mysql
android·python
Couvrir洪荒猛兽3 小时前
Android实训十 数据存储和访问
android
五味香5 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录5 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽7 小时前
Android实训九 数据存储和访问
android
aloneboyooo7 小时前
Android Studio安装配置
android·ide·android studio
Jacob程序员8 小时前
leaflet绘制室内平面图
android·开发语言·javascript
2401_897907868 小时前
10天学会flutter DAY2 玩转dart 类
android·flutter