WiFi相关操作

QuibblerQuibbler 2020-01-14 约 23 分钟 2709 次阅读

WiFi相关操作

前段时间学习了Android中WiFi相关操作,一直没来得及写。乘着过年只能在家待写一写。关于WiFi扫描、WiFi基本信息、WiFi连接等操作。

1、所需权限

必须在AndroidManifest中申请以下3种权限,缺一不可。缺一个都无法扫描或者得到扫描结果。详细权限需求说明查看官方API。

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

最好再动态监测一下有没有相应的权限,如果没有就动态申请。这里不再累述。

2、WifiManager

        

2.1、获取WifiManager

对WiFi的操作需要通过WifiManager对象来完成,getSystemService()获取WifiManager对象:

WifiManager mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);

2.2、WiFiManager常用方法

WifiManager封装了大量方便的API,方便我们操作WiFi

//添加一个config描述的WIFI网络,默认情况下,这个WIFI网络是DISABLE状态的。
    addNetwork(WifiConfiguration config) 

    //计算信号的等级
    calculateSignalLevel(int rssi , int numLevels) 

    //对比网络A和网络B的信号强度    
    compareSignalLevel(int rssiA, int rssiB) 

    //创建一个WIFI 锁,锁定当前的WIFI连接    
    createWifiLock(int lockType, String tag) 

    //让一个网络连接失效
    disableNetwork(int netId) 

    //断开当前的WIFI连接
    disconnect() 

    //连接netId所指的WIFI网络,并是其他的网络都被禁用
    enableNetwork(int netId, Boolean disableOthers) 

    //获取网络连接的状态
    getConfiguredNetworks() 

    //获取当前连接的信息
    getConnectionInfo() 

    //获取DHCP 的信息
    getDhcpInfo() 

    //获取扫描测试的结果
    getScanResulats() 

    //获取当前WIFI设备的状态
    getWifiState() 

    //判断WIFI设备是否打开
    isWifiEnabled() 

    //ping操作,和PC的ping操作相同作用
    pingSupplicant() 

    //重新连接WIFI网络,即使该网络是已经被连接上的
    ressociate() 

    //重新连接一个未连接上的WIFI网络
    reconnect() 

    //移除某一个网络
    removeNetwork() 

    //保留一个配置信息
    saveConfiguration() 

    //让一个连接有效
    setWifiEnabled() 

    //开始扫描
    startScan() 

    //更新一个网络连接
    updateNetwork(WifiConfiguration config)

3、获取WiFi状态

获取当前WiFi设备的状态,使用getWifiState()方法

int wifiStatus = mWifiManager.getWifiState();

在WifiManager中定义了5中WiFi状态:

/**
     * WIFI网卡正在关闭
     */
    public static final int WIFI_STATE_DISABLING = 0;
    
    /**
     * WIFI网卡不可用
     */
    public static final int WIFI_STATE_DISABLED = 1;
    
    /**
     * WIFI网卡正在打开
     */
    public static final int WIFI_STATE_ENABLING = 2;
    
    /**
     * WIFI网卡可用
     */
    public static final int WIFI_STATE_ENABLED = 3;
    
    /**
     * WIFI网卡状态不可知
     */
    public static final int WIFI_STATE_UNKNOWN = 4;

还可以调用isEnable()查看当前WiFi是否打开

boolean isEnable = mWifiManager.isWifiEnabled();

4、扫描WiFi

扫描WiFi很简单,直接调用WifiManager的startScan()方法即可开启扫描,会有一个返回值,是否开启扫描。如果没有开启定位,startScan()总会返回false,无法扫描附近的WiFi。

boolean isStartScan = mWifiManager.startScan();

该方法已经被废弃@deprecated,目前还能使用。官方文档说的是

Added in API level 1
    Deprecated in API level 28
    @deprecated The ability for apps to trigger scan requests will be removed in a future release.

如果或得扫描结果呢?调用WifiManager的getScanResults(),返回扫描结果的集合。

List<ScanResult> mWifiScanResult = mWifiManager.getScanResults();

ScanResult也定义在android.net.wifi包中,打印ScanResult对象可以看到扫描到的WiFi信息:

SSID: 106,                //网络的名字,唯一区别WIFI网络的名字
    BSSID: 8c:a6:df:2e:5a:74, //接入点的地址
    capabilities: [WPA2-PSK-CCMP][WPA-PSK-CCMP][ESS],  //网络接入的性能
    level: -61,               //所发现的WIFI网络信号强度
    frequency: 2412,          //当前WIFI设备附近热点的频率(MHz)
    timestamp: 247485116699, 
    distance: ?(cm), 
    distanceSd: ?(cm), 
    passpoint: no, 
    ChannelBandwidth: 1, 
    centerFreq0: 2422, 
    centerFreq1: 0, 
    80211mcResponder: is not supported, 
    Carrier AP: no, 
    Carrier AP EAP Type: -1, 
    Carrier name: null, 
    Radio Chain Infos: [RadioChainInfo: id=0, level=-61]

通过ScanResult对象也可以获取这些信息,ScanResult并没有提供这些字段的getter方法,因为这些字段在ScanResult类中的定义都是public的,可以直接访问。

不过不推荐startScan()之后,立马调用getScanResult()去获取结果。这样获取到的是上一次扫描的结果,而应该监听扫描结束发出的广播,扫描结束后再去获取结果。

首先定义一个广播接收器:

private BroadcastReceiver mWifiScanResultReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (null != action && action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                //接收到扫描结束发出的广播,获取WiFi扫描结果
                mWifiScanResult = mWifiManager.getScanResults();
            }
        }
    };

onResume中注册广播接收器,接收WifiManager.SCAN_RESULTS_AVAILABLE_ACTION广播:

@Override
    protected void onResume() {
        super.onResume();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
        registerReceiver(mWifiScanResultReceiver, intentFilter);
    }

最后,别忘了在onPause()中反注册广播接收器:

@Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mWifiScanResultReceiver);
    }

Tips:事实证明开启热点后,可以扫描WiFi,但是始终是空[]。似乎不开启WiFi也能扫描附近的WiFi信息。

5、WifiInfo:连接信息

android.net.wifi包中定义了很多有用的类,其中WifiInfo类描述活动的或正在建立的任何Wifi连接的状态。

WifiInfo wifiInfo = mWifiManager.getConnectionInfo();

调用getConnectionInfo()获取当前WiFi连接的信息。如果没有任何连接建立,也不会返回空的WiFiInfo而是返回一个默认的WiFiInfo对象。

//没有任何连接返回的WiFiInfo打印出来
    SSID: <unknown ssid>,
    BSSID: <none>,
    MAC: 02:00:00:00:00:00,
    Supplicant state: DISCONNECTED, 
    RSSI: -127, 
    Link speed: -1Mbps, 
    Frequency: -1MHz, 
    Net ID: -1, 
    Metered hint: false, 
    score: 0

其中的字段可以通过WiFiInfo对象获取:

获取BSSID属性
    getBSSID() 

    获取客户端的连通性
    getDetailedStateOf() 

    获取SSID 是否被隐藏
    getHiddenSSID() 

    获取IP 地址
    getIpAddress() 

    获取连接的速度
    getLinkSpeed() 

    获取Mac 地址
    getMacAddress() 


    getRssi() 

    获取SSID
    getSSID() 

    获取具体客户端状态的信息
    getSupplicanState()

6、WiFiConfiguration

WifiConfiguration用来配置网络,配置有密码的WiFiWifiConfiguration方法:

/**
   * 连接有密码的wifi.
   *
   * @param SSID     ssid
   * @param Password Password
   * @return apConfig
   */
  private WifiConfiguration setWifiParamsPassword(String SSID, String Password) {
    WifiConfiguration apConfig = new WifiConfiguration();
    apConfig.SSID = "\"" + SSID + "\"";
    apConfig.preSharedKey = "\"" + Password + "\"";
    //不广播其SSID的网络
    apConfig.hiddenSSID = true;
    apConfig.status = WifiConfiguration.Status.ENABLED;
    //公认的IEEE 802.11验证算法。
    apConfig.allowedAuthAlgorithms.clear();
    apConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
    //公认的的公共组密码
    apConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
    apConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
    //公认的密钥管理方案
    apConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
    //密码为WPA。
    apConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
    apConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
    //公认的安全协议。
    apConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
    return apConfig;
  }

连接没有密码wifi

/**
   * 连接没有密码wifi.
   *
   * @param ssid ssid
   * @return configuration
   */
  private WifiConfiguration setWifiParamsNoPassword(String ssid) {
    WifiConfiguration configuration = new WifiConfiguration();
    configuration.SSID = "\"" + ssid + "\"";
    configuration.status = WifiConfiguration.Status.ENABLED;
    configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
    configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
    configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
    configuration.allowedPairwiseCiphers
        .set(WifiConfiguration.PairwiseCipher.TKIP);
    configuration.allowedPairwiseCiphers
        .set(WifiConfiguration.PairwiseCipher.CCMP);
    configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
    return configuration;
  }

7、WiFi连接

首先addNetwork()添加一个网络,返回这个网络的id,连接网络的时候需要用到。

int id = mWifiManager.addNetwork(WifiConfiguration config)

该方法也被标记为deprecate,在Android Q API 29之后只会返回-1。

Android Q or above, this API will always return -1.

然后enableNetwork(id,boolean),尝试连接指定id的网络。

/**
     *the ID of the network as returned by addNetwork or getConfiguredNetworks.
     */
    boolean enableResult = mWifiManager.enableNetwork(id, true);

8、扫描频率限制(耗电)

扫描WiFi是一个很耗电的操作,Android在官方文档中也明确给出了限制说明。

相关资料:

        android.net.wifi

        android.net.wifi.WifiInfo

        ../packages/apps/Settings /src/com/android/settings/wifi

        ../frameworks/base/wifi/

        ../frameworks/opt/net/wifi/

        ../frameworks/opt/net/wifi//service/jni/

        ../system/connectivity/wificond

        ../system/connectivity/wifilogd/

        ../hardware/libhardware_legacy/include/hardware_legacy/

        《Wifi模块—源码分析Wifi热点扫描(Android P)》

        《Android O WiFi的扫描流程梳理续——梳理java与c++之间的aidl-cpp通信》

        《android中wifi原理及流程分析(很经典)》

        《Android 8.0 wifi架构》

        《WIFI 概览》

        Android P WLAN 从框架层到HAL层

        Android WLAN 开关和连接流程分析(一) 应用层

        Android WLAN 开关和连接流程分析(二) 框架层

        wap_supplicant介绍

        Android WIFI功能——WifiManager

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w

评论 (1)

评论功能已关闭,仅展示历史评论
Quibbler
Quibbler

        更多WiFi状态变化,可以通过广播接收获取。参考海晨忆博客的代码段:

/**
 * Author: 海晨忆.
 * Date: 2018/1/10
 * Desc: 网络广播监听
 */
public class NetworkReceiver extends BroadcastReceiver {
  public static final int STATE1 = 1;//密码错误
  public static final int STATE2 = 2;//连接成功
  public static final int STATE3 = 3;//连接失败
  public static final int STATE4 = 4;//正在获取ip地址
  public static final int STATE5 = 5;//正在连接
  @Override
  public void onReceive(Context context, Intent intent) {
    if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(intent.getAction())) {
      //密码错误广播,是不是正在获得IP地址
      int linkWifiResult = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
      if (linkWifiResult == WifiManager.ERROR_AUTHENTICATING) {
        //密码错误
        ToastUtils.showTipMsg(R.string.password_wrong);
        sendNetworkStateChange(new NetWorkInfo().setState(STATE1));
      }
      SupplicantState supplicantState = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
      NetworkInfo.DetailedState state = WifiInfo.getDetailedStateOf(supplicantState);
      Log.v(Constants.HTTP_WZ, state.name());
      if (state == NetworkInfo.DetailedState.CONNECTING) {
        //正在连接
        ToastUtils.showTipMsg(R.string.linking);
        sendNetworkStateChange(new NetWorkInfo().setState(STATE5));
      } else if (state == NetworkInfo.DetailedState.FAILED
          || state == NetworkInfo.DetailedState.DISCONNECTING) {
        //连接失败
        sendNetworkStateChange(new NetWorkInfo().setState(STATE3));
        ToastUtils.showTipMsg(R.string.linked_failed);
      } else if (state == NetworkInfo.DetailedState.CONNECTED) {
        //连接成功
        ToastUtils.showTipMsg(R.string.linked_success);
      } else if (state == NetworkInfo.DetailedState.OBTAINING_IPADDR) {
        //正在获取ip地址
        sendNetworkStateChange(new NetWorkInfo().setState(STATE4));
      } else if (state == NetworkInfo.DetailedState.IDLE) {
        //闲置的
        ConnectivityManager connectManager = (ConnectivityManager) BaseApplication.getInstance()
            .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetworkInfo = null;
        if (connectManager != null) {
          activeNetworkInfo = connectManager.getActiveNetworkInfo();
        }
        if (activeNetworkInfo == null) {
          sendNetworkStateChange(new NetWorkInfo().setState(STATE3));
          ToastUtils.showTipMsg(R.string.linked_failed);
        }
      }
    } else if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
      // 监听wifi的打开与关闭,与wifi的连接无关
      int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);
      Log.v(Constants.HTTP_WZ, "wifiState:" + wifiState);
      switch (wifiState) {
        case WifiManager.WIFI_STATE_DISABLING://正在停止0
          ToastUtils.showTipMsg(R.string.close_wifi);
          break;
        case WifiManager.WIFI_STATE_DISABLED://已停止1
          break;
        case WifiManager.WIFI_STATE_UNKNOWN://未知4
          break;
        case WifiManager.WIFI_STATE_ENABLING://正在打开2
          ToastUtils.showTipMsg(R.string.opening_wifi);
          break;
        case WifiManager.WIFI_STATE_ENABLED://已开启3
          break;
        default:
          break;
      }
    } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) {
      // 监听wifi的连接状态即是否连上了一个有效无线路由
      Parcelable parcelableExtra = intent
          .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
      if (null != parcelableExtra) {
        // 获取联网状态的NetWorkInfo对象
        NetworkInfo networkInfo = (NetworkInfo) parcelableExtra;
        //获取的State对象则代表着连接成功与否等状态
        NetworkInfo.State state = networkInfo.getState();
        //判断网络是否已经连接
        boolean isConnected = state == NetworkInfo.State.CONNECTED;
        Log.v(Constants.HTTP_WZ, "isConnected:" + isConnected);
        if (isConnected) {
          ToastUtils.showTipMsg(R.string.linked_success);
          sendNetworkStateChange(new NetWorkInfo().setState(STATE2));
        }
      }
    }
  }
  /**
   * 发送网络状态eventBus.
   *
   * @param info info
   */
  private void sendNetworkStateChange(NetWorkInfo info) {
    EventBus.getDefault().post(info);
  }
  @SuppressWarnings("unused")
  private String getConnectionType(int type) {
    String connType = "";
    if (type == ConnectivityManager.TYPE_MOBILE) {
      connType = "移动网络";
    } else if (type == ConnectivityManager.TYPE_WIFI) {
      connType = "WIFI网络";
    }
    return connType;
  }
  public static class NetWorkInfo {
    public int state;//1密码错误,2:连接成功,3:连接失败
    private NetWorkInfo setState(int state) {
      this.state = state;
      return this;
    }
  }
}