WiFi开发使用介绍

目录

前言

WiFi权限

WiFi的开启与关闭

WiFi扫描及ResultList获取

WiFi扫描

ResultList获取

WiFi连接

WiFi流程和代码封装

WiFi的连接状态判断


前言

公司设备产品不同于互联网移动端项目开发,设备端的WiFi、蓝牙相关功能需要在咱们的应用开发中进行实现和定制,故我们必须熟悉WiFi、蓝牙等相关标准开发流程,以减少在开发过程和测试过程因为流程不规范而引入的问题。

本文主要讨论的是WiFi的开关、扫描、连接等相关流程,以Android4.3 至Android6.0系统设置WiFi相关流程作为参考。Android各个版本的WiFi相关设置有些出入,但是大致流程不变,流程供参考,若有借鉴请自行充分验证并查阅对应系统WiFi设置源码进行对比。系统源码在线查阅地址,Android 7.1以后:CPC | 设备控制台,Android 9.0之前:AndroidXRef

WiFi权限

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

WiFi的开启与关闭

  • 系统设置中WiFi开启与关闭控制类:

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

  • 这个类主要控制的是WiFi UI按钮的状态和WiFi的状态。以下是核心代码整理如下:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiManager;
import android.widget.CompoundButton;
import android.widget.Switch;
​
public class WifiEnabler implements CompoundButton.OnCheckedChangeListener {
​
    private final Context mContext;
    private Switch mSwitch;
​
    private final WifiManager mWifiManager;
    private boolean mStateMachineEvent;
    private final IntentFilter mIntentFilter;
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
                handleWifiStateChanged(intent.getIntExtra(
                        WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN));
            }
        }
    };
​
​
    public WifiEnabler(Context context, Switch switch_) {
        mContext = context;
        mSwitch = switch_;
​
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
        // The order matters! We really should not depend on this. :(
        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    }
​
    //
    public void resume() {
        // Wi-Fi state is sticky, so just let the receiver update UI
        mContext.registerReceiver(mReceiver, mIntentFilter);
        mSwitch.setOnCheckedChangeListener(this);
    }
​
    public void pause() {
        mContext.unregisterReceiver(mReceiver);
        mSwitch.setOnCheckedChangeListener(null);
    }
    /**
     * 设置UI WiFi按钮,以便在这里控制按钮状态
     */
    public void setSwitch(Switch switch_) {
        if (mSwitch == switch_) return;
        mSwitch.setOnCheckedChangeListener(null);
        mSwitch = switch_;
        mSwitch.setOnCheckedChangeListener(this);
​
        final int wifiState = mWifiManager.getWifiState();
        boolean isEnabled = wifiState == WifiManager.WIFI_STATE_ENABLED;
        boolean isDisabled = wifiState == WifiManager.WIFI_STATE_DISABLED;
        mSwitch.setChecked(isEnabled);
        mSwitch.setEnabled(isEnabled || isDisabled);
    }
​
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        //Do nothing if called as a result of a state machine event
        if (mStateMachineEvent) {
            return;
        }
        // ① 如果在飞行模式下等其他不允许使用 Wi-Fi的情况,则显示 toast 消息
        if (isChecked && isWifiForbidden()) {
            //将开关重置为关闭。没有无限的 checklistenr 循环。
            buttonView.setChecked(false);
        }
​
        //② 如果启用 Wifi,则禁用热点网络共享
        if (isChecked && isWiFiAPOpen()) {
            //关闭热点 TODO
        }
​
        if (mWifiManager.setWifiEnabled(isChecked)) {
            // 考虑进行中的状态,禁用直到新状态处于活动状态
            mSwitch.setEnabled(false);
        } else {
            // Error
        }
    }
​
    private boolean isWifiForbidden() {
        //...
        return false;
    }
    private boolean isWiFiAPOpen() {
        //... 
        return false;
    }
​
    private void handleWifiStateChanged(int state) {
        switch (state) {
            case WifiManager.WIFI_STATE_ENABLING:
                mSwitch.setEnabled(false);
                break;
            case WifiManager.WIFI_STATE_ENABLED:
                setSwitchChecked(true);
                mSwitch.setEnabled(true);
                break;
            case WifiManager.WIFI_STATE_DISABLING:
                mSwitch.setEnabled(false);
                break;
            case WifiManager.WIFI_STATE_DISABLED:
                setSwitchChecked(false);
                mSwitch.setEnabled(true);
                break;
            default:
                setSwitchChecked(false);
                mSwitch.setEnabled(true);
                break;
        }
    }
​
    private void setSwitchChecked(boolean checked) {
        if (checked != mSwitch.isChecked()) {
            mStateMachineEvent = true;
            mSwitch.setChecked(checked);
            mStateMachineEvent = false;
        }
    }
}

使用时创建一个对象,在合适的地方调用resume()和pause(),系统设置是在对应activity生命周期中调用的。

WiFi扫描及ResultList获取

WiFi扫描

  • 系统设置中WiFi扫描的控制类为内部类Scanner。

    • Android 6.0以前在 Scanner在

      "/packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java"中;

    • Android 6.0及以后 Scanner在

      "/frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java"中

  • 该类主要控制WiFi的扫描逻辑和流程,核心代码整理如下:

    class Scanner extends Handler {
            static final int MSG_SCAN = 0;
    ​
            private int mRetry = 0;
    ​
            void resume() {
                if (!hasMessages(MSG_SCAN)) {
                    sendEmptyMessage(MSG_SCAN);
                }
            }
    ​
            void forceScan() {
                removeMessages(MSG_SCAN);
                sendEmptyMessage(MSG_SCAN);
            }
    ​
            void pause() {
                mRetry = 0;
                removeMessages(MSG_SCAN);
            }
    ​
            boolean isScanning() {
                return hasMessages(MSG_SCAN);
            }
    ​
            @Override
            public void handleMessage(Message message) {
                if (message.what != MSG_SCAN) return;
                if (mWifiManager.startScan()) {
                    mRetry = 0;
                } else if (++mRetry >= 3) {
                    mRetry = 0;
                    //TODO 通知用户扫描失败
                    return;
                }
                sendEmptyMessageDelayed(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
            }
        }
    // Combo scans can take 5-6s to complete - set to 10s
    //(组合扫描可能需要 5-6 秒才能完成 - 设置为 10 秒)
    private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;

    使用时创建一个对象,在合适的地方调用resume()和pause(),系统设置是在对应activity生命周期中调用的,及对应的广播状态中调用。

    这里有个注意点,即在WiFi正在获取IP地址是的状态不要进行WiFi扫描;故对scan方法封装如下:

    private void scan() {
        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
        NetworkInfo.DetailedState detailedState = wifiInfo.getDetailedStateOf(wifiInfo.getSupplicantState());
        //获取IP中并且没有连接上则跳过本次扫描
        if (!(detailedState == NetworkInfo.DetailedState.OBTAINING_IPADDR )||  WifiUtils.isConnect(this)) {
            mWifiUtils.startScan();
        } else {
            Log.w(TAG, "scan canceled , wifi detailedState = " + detailedState);
        }
    }

    ResultList获取

    WiFi扫描指令(mWifiManager.startScan)发起后,系统会进行扫描动作,扫描完成会发送一个扫描完成的广播,在收到扫描完成广播后再去获取系统扫描的WiFi缓存列表。相关流程代码梳理如下:

    //广播注册
    mFilter = new IntentFilter();
    //add actions
    mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    ​
    //SCAN_RESULTS_AVAILABLE_ACTION 就是WiFi扫描已完成,并且结果可用的Action
    mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    ​
    mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
    mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
    ​
    mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            handleEvent(context, intent);
        }
    };
    ​
    //广播处理及列表获取
    private void handlerEvent(Context context,Intent intent){
        String action = intent.getAction();
        if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
            int wifiState = mWifiManager().getWifiState();
            switch (wifiState) {
                case WifiManager.WIFI_STATE_ENABLED: {
                    mResultList = mWifiManager.getScanResults();
                    break;
                case WifiManager.WIFI_STATE_DISABLED:
                     //...
                    break;
                case WifiManager.WIFI_STATE_DISABLING:
                     //...
                    break;
                case WifiManager.WIFI_STATE_ENABLING:
                     //...
                    break;
                case WifiManager.WIFI_STATE_UNKNOWN:
                     //...
                    break;
            }
        }else{
                //...
        }
    }

    WiFi连接

    WiFi流程和代码封装

    WiFi连接是根据WiFi SSID和password 以及加密类型生成WiFi配置信息,然后通过配置信息来保存连接网络的,这里参照系统WiFi设置里的实习方式以及项目中相关封装代码,整理如下:

    /**
     * @Description 连接网络
     * @param SSID ssid
     * @param password 密码
     * @param type
     *  WIFI_CONNECT_NO_PWD = 1
     *  WIFI_CONNECT_WEP = 2
     *  WIFI_CONNECT_WPA = 3
     * @return
     */
    public int connect(String SSID, String password, int type) {
        //删除旧选项;
        WifiConfiguration tempConfig = isExsits(SSID);
        if (tempConfig != null) {
            mWifiManager.removeNetwork(tempConfig.networkId);
        }
        WifiConfiguration wifiConfig = createWifiInfo(SSID, password, type);
        addNetwork(wifiConfig);
        WifiConfiguration targetConfigure = isExsits(SSID);
        if (targetConfigure != null) {
            int networkId = targetConfigure.networkId;
            return networkId;
        }
        return -1;
    }
    ​
    /**
     * @Description 检查该网络配置文件是否存在
     * @param SSID
     * @return
     */
    public WifiConfiguration isExsits(String SSID) {
        List<WifiConfiguration> existingConfigs = mWifiManager.getConfiguredNetworks();
        if(existingConfigs == null){
            return null;
        }
        for (WifiConfiguration existingConfig : existingConfigs) {
            if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
                return existingConfig;
            }
        }
        return null;
    }
    ​
    /**
     * @Description 创建一个网络配置文件
     * @param SSID
     * @param password
     * @param type
     * @return 返回网络配置
     */
    public WifiConfiguration createWifiInfo(String SSID, String password, int type) {
        WifiConfiguration config = new WifiConfiguration();
        config.allowedAuthAlgorithms.clear();
        config.allowedGroupCiphers.clear();
        config.allowedKeyManagement.clear();
        config.allowedPairwiseCiphers.clear();
        config.allowedProtocols.clear();
        config.SSID = "\"" + SSID + "\"";
        if (type == 1) {// NO_PASWORD
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
        }
        if (type == 2) {// WEP加密
            config.hiddenSSID = true;
            config.wepKeys[0] = "\"" + password + "\"";
            config.allowedAuthAlgorithms
                    .set(WifiConfiguration.AuthAlgorithm.SHARED);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
            config.allowedGroupCiphers
                    .set(WifiConfiguration.GroupCipher.WEP104);
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
            config.wepTxKeyIndex = 0;
        }
        if (type == 3) {// WPA/WPA2加密
            config.preSharedKey = "\"" + password + "\"";
            config.hiddenSSID = true;
            config.allowedAuthAlgorithms
                    .set(WifiConfiguration.AuthAlgorithm.OPEN);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            config.allowedPairwiseCiphers
                    .set(WifiConfiguration.PairwiseCipher.TKIP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedPairwiseCiphers
                    .set(WifiConfiguration.PairwiseCipher.CCMP);
            config.status = WifiConfiguration.Status.ENABLED;
        }
        return config;
    }
    ​
    /**
     * @Description 添加一个网络并连接
     * @param wcg
     */
    public boolean addNetwork(WifiConfiguration wcg) {
        int wcgID = mWifiManager.addNetwork(wcg);
        if (wcgID != -1) {
            mWifiManager.enableNetwork(wcgID, true);
            mWifiManager.saveConfiguration();
            mWifiManager.reconnect();
            Log.d(TAG, "addNetwork: reconnect");
            return true;
        }
        return false;
    }

    连接过程结果判断,可以通过以下【WiFi的连接状态判断】来获取。

:若是成功,以下两种都是可以,但是连接失败在Android 6.0以前是不会获取到连接失败的广播的,故Android 6.0以前最好做一个超时定时器进行循环主动查询判断。

WiFi的连接状态判断

    1. 连接状态被动接收。

    通过注册WiFi状态广播进行接收。

    if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
        int linkWifiResult = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
        if (linkWifiResult == WifiManager.ERROR_AUTHENTICATING) {
            if (currentScanResult != null && currentScanResult.BSSID.equals(wifiInfo.getBSSID())) {
                hideLoadingDialog();
                if (timeOutTimer != null) {
                    timeOutTimer.cancel();
                }
                alertPsdErrorDialog();
                // ...
            }
        }
       
    } else 
        /**
         * 描  述 此段代码是基于Android6.0新增密码错误广播,满足现有需求
         */
    if ( "android.net.wifi.supplicant.PASSWARD_ERROR".equals(action)) {
        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
        if (currentScanResult != null && currentScanResult.BSSID.equals(wifiInfo.getBSSID())) {
            hideLoadingDialog();
            if (timeOutTimer != null) {
                timeOutTimer.cancel();
            }
            alertPsdErrorDialog();                  
             // ...
        }
     /**
      *NETWORK_STATE_CHANGED_ACTION:WIFI连接状态发生改变的广播.可以从intent中取得NetworkInfo
      *此时 NetworkInfo 中提供了连接的新状态,如果连接成功, 可以获取当前连接网络的 BSSID,和WifiInfo.
      */
    }else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
        //网络状态改变...
    } else{
        //...
    }

    1. 主动查询

    通过WiFiManager相关Api进行WiFi连接状态查询。代码封装如下:

    /**
     * @Description 判断当前设备是否连接上了对应wifi热点
     * @param context
     * @param wifiHotspotName WiFi热点名称
     * @return
     */
    public boolean isWifiConnected(Context context, String wifiHotspotName) {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        if (info != null) {
            String extraInfo = info.getExtraInfo();
            if (extraInfo.equals("\"" + wifiHotspotName + "\"")) {
                return true;
            }
        }
        return false;
    }
    ​
    /**
     * @Description 判断网络是否连接成功
     * @param context
     * @return
     */
    public static boolean isConnect(Context context) {
        ConnectivityManager connMgr = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
        if (activeInfo != null && activeInfo.isConnected()) {
            return activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
        }
        return false;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值