Android WIFI使用简述
2023-03-04 13:02:23 来源:腾讯云

前言

随着Android版本的更新,目前最新的版本是Android 13,并且已经有部分国产手机更新了此版本,对于Android开发者来说,变化其实不那么大,而对于本文章来说就有一些变化。

正文

在Android 12版本中,增加了对于蓝牙操作的动态权限,而在Android 13中,增加了对于WIFI操作的动态权限,日常工作生活中,我们用到WIFI功能是很多的,例如手机、电脑、电视等设备。而使用WIFI是一回事,WIFI开发又是另一回事,和蓝牙是一个道理,它们之间也有很多相似的地方。


【资料图】

一、创建项目

首先创建项目,这里我使用的Android Studio版本为Android Studio Electric Eel | 2022.1.1,创建一个名为Android13Wifi的项目。

项目创建好之后,最低的API为24对应Android 7,最高33对应Android 13,Gradle插件版本为:7.5。

二、配置项目

作为WIFI项目我们首先要配置项目的静态权限,在AndroidManifest.xml中增加如下代码:

                

在 Android 13 中,Google 将 Wi-Fi 扫描与位置相关内容分离, Android 13 为管理设备与周围 Wi-Fi 热点连接的应用添加 NEARBY_WIFI_DEVICES运行时权限 (属于 NEARBY_DEVICES权限组),从而在不需要 ACCESS_FINE_LOCATION权限的情况下,也可以让应用访问附近的 Wi-Fi 设备。

这和Android 12中增加的三个蓝牙权限如出一辙,此前扫描蓝牙和WIFI需要定位权限一直是Google的痛点,也一直被诟病。

所以对于仅需要连接 Wi-Fi 设备,但实际上并不需要了解设备位置的应用来说,以 Android 13 (33)为目标平台的应用现在可以通过 “neverForLocation” 属性来完善申请 NEARBY_WIFI_DEVICES权限。

同时我们还应该关注Android 13以下的设备使用,因此ACCESS_FINE_LOCATION权限也要配置,在AndroidManifest.xml中增加如下代码:

        

然后可以开启ViewBinding,在app下的build.gradle的android{}闭包中增加如下代码:

buildFeatures {        viewBinding true    //开启ViewBinding    }

添加位置如下图所示:

然后我们修改以下activity_main.xml,里面的代码如下所示:

    

就只有两个按钮(用于打开/关闭WIFI,扫描WIFI),一个列表(显示WIFI设备,连接WIFI)。现在xml已经有了,我们先搞定ViewBinding,修改MainActivity的代码如下所示:

public class MainActivity extends AppCompatActivity {    private ActivityMainBinding binding;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        binding = ActivityMainBinding.inflate(getLayoutInflater());        setContentView(binding.getRoot());    }}

下面就是写功能了,首先是WIFI的打开和关闭,在此之前需要获取WIFI的开关状态。

三、WIFI开关

在使用Wifi之前,我们首先要打开Wifi,而打开Wifi在不同的版本上方式不同,首先在MainActivity中声明变量

private WifiManager wifiManager;//Wifi管理者    private ActivityResultLauncher openWifi;    //打开Wifi意图

然后通过系统Wifi服务获取wifiManager,在onCreate()方法中添加如下代码:

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

通过wifiManager.getWifiState()可以得到Wifi的状态,所以可以在MainActivity中 checkWifiState()方法,代码如下所示:

public void checkWifiState() {        String msg;        switch (wifiManager.getWifiState()) {            case WifiManager.WIFI_STATE_DISABLING:                msg = "Wifi正在关闭";                break;            case WifiManager.WIFI_STATE_DISABLED:                msg = "Wifi已经关闭";                binding.btnOpenWifi.setText("打开Wifi");                break;            case WifiManager.WIFI_STATE_ENABLING:                msg = "Wifi正在开启";                break;            case WifiManager.WIFI_STATE_ENABLED:                msg = "Wifi已经开启";                binding.btnOpenWifi.setText("关闭Wifi");                break;            default:                msg = "没有获取到WiFi状态";                break;        }        showMsg(msg);    }

这里我在Wifi开启和关闭的时候修改了按钮的文字,因为涉及到Android版本的判断,所以在MainActivity中增加isAndroidTarget()方法,代码如下所示:

private boolean isAndroidTarget(int targetVersion) {        return Build.VERSION.SDK_INT >= targetVersion;    }

通过这个方法我们只要传递目标Android版本进去即可,最后再增加一个showMsg()方法,用于弹出Toast进行提示。

private void showMsg(CharSequence msg) {        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();    }

在Android10及以上版本打开蓝牙开关需要进行一个意图处理,这里我们通过Activity Result API来进行处理,在MainActivity中声明变量:

private ActivityResultLauncher openWifi;    //打开Wifi意图

然后新增一个registerIntent()方法,代码如下所示:

private void registerIntent() {        //打开Wifi开关        openWifi = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {            checkWifiState();        });    }

在返回结果时,调用checkWifiState()方法检查Wifi的状态,registerIntent()方法需要在Activity创建之前进行调用,修改onCreate()方法,代码如下:

@Override    protected void onCreate(Bundle savedInstanceState) {    //注册意图        registerIntent();        super.onCreate(savedInstanceState);        binding = ActivityMainBinding.inflate(getLayoutInflater());        setContentView(binding.getRoot());        //通过Wifi服务获取wifi管理者对象        wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);        //初始化视图        initView();    }

这里有一个initView()方法,在MainActivity中创建它,在此方法中将对于按钮的点击事件进行处理,代码如下所示:

private void initView() {        //打开/关闭Wifi        binding.btnOpenWifi.setOnClickListener(v -> {            //Android10及以上版本            if (isAndroidTarget(Build.VERSION_CODES.Q)) {                openWifi.launch(new Intent(Settings.Panel.ACTION_WIFI));            } else {                wifiManager.setWifiEnabled(!wifiManager.isWifiEnabled());                checkWifiState();            }        });    }

这里在点击事件中判断了当前的Android版本,10及以上版本采用意图的方式,以下的版本采用wifiManager.setWifiEnabledAPI的方式,下面我们运行一下:

四、WIFI扫描

WIFI开关搞定之后,我们来做WIFI的扫描,这里的WIFI扫描是通过广播来接收结果,结果对象是ScanResult,这个名字和蓝牙扫描的ScanResult一样,不要导错了包,扫描的结果以列表的形式展现,所以我们可以根据这个结果对象来写一个Wifi适配器,适配器中就显示Wifi的名称,状态,信号强度信息。这里会用到比较多的图片资源,用来标识信号强度等级的,从我的源码中去获取即可。

根据Wifi的加密与否,分为两种:加密与开放,每一种有五个图标来分别表示不同的信号强度,这里我做了两个level-list,是wifi_level.xml和wifi_lock_level.xml,在代码中可以通过信号强度得到不同的level,然后根据加密状态设置level资源图标即可。

① Wifi适配器

要创建适配器,首先要创建item的xml,在layout下创建一个item_wifi_rv.xml,代码如下所示:

            androidx.constraintlayout.widget.ConstraintLayout>

如我前面所说的,就是三个内容

下面写适配器,在com.llw.wifi下创建一个WifiAdapter类,代码如下所示:

public class WifiAdapter extends RecyclerView.Adapter {    private final List lists;    public WifiAdapter(List lists) {        this.lists = lists;    }    @NonNull    @Override    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {        ItemWifiRvBinding binding = ItemWifiRvBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);        return new ViewHolder(binding);    }    @Override    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {        //Wifi名称        holder.binding.tvWifiName.setText(lists.get(position).SSID);        //Wifi功能        String capabilities = lists.get(position).capabilities;        //Wifi状态标识 true:加密,false:开放        boolean wifiStateFlag = capabilities.contains("WEP") || capabilities.contains("PSK") || capabilities.contains("EAP");        //Wifi状态描述        String wifiState = wifiStateFlag ? "加密" : "开放";        holder.binding.tvWifiState.setText(wifiState);        //信号强度        int imgLevel;        int level = lists.get(position).level;        if (level <= 0 && level >= -50) {            imgLevel = 5;        } else if (level < -50 && level >= -70) {            imgLevel = 4;        } else if (level < -70 && level >= -80) {            imgLevel = 3;        } else if (level < -80 && level >= -100) {            imgLevel = 2;        } else {            imgLevel = 1;        }        //根据是否加密设置不同的图片资源        holder.binding.ivSignal.setImageResource(wifiStateFlag ? R.drawable.wifi_lock_level : R.drawable.wifi_level);        //设置图片等级        holder.binding.ivSignal.setImageLevel(imgLevel);    }    @Override    public int getItemCount() {        return lists.size();    }    public static class ViewHolder extends RecyclerView.ViewHolder {        public ItemWifiRvBinding binding;        public ViewHolder(@NonNull ItemWifiRvBinding itemWifiRvBinding) {            super(itemWifiRvBinding.getRoot());            binding = itemWifiRvBinding;        }    }}

这里就简单用了一下ViewBinding,核心的内容在onBindViewHolder()方法中,这里我们根据扫描结果进行三个内容的渲染。

② 扫描结果处理

下面我们回到MainActivity,声明变量:

private final List wifiList = new ArrayList<>();    //Wifi结果列表    private WifiAdapter wifiAdapter;    //Wifi适配器

扫描成功或者适配都可以处理,在MainActivity中新增如下方法代码:

private void scanSuccess() {        //扫描成功:新的扫描结果        wifiList.clear();        wifiList.addAll(wifiManager.getScanResults());        wifiAdapter.notifyDataSetChanged();    }    private void scanFailure() {        // 处理失败:新的扫描没有成功,使用旧的扫描结果        wifiList.clear();        wifiList.addAll(wifiManager.getScanResults());        wifiAdapter.notifyDataSetChanged();    }

你会发现两个方法的内容是一样的,但是含义不同,当然你如果了解的话,就使用一个方法也行。下面在MainActivity中新增如下代码:

/**     * Wifi扫描广播接收器     */    private final BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context c, Intent intent) {            boolean success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);            if (success) {                scanSuccess();            } else {                scanFailure();            }        }    };

通过接收广播来刷新数据,在MainActivity中再增加一个initScan(),代码如下所示:

private void initScan() {        IntentFilter intentFilter = new IntentFilter();        intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);        registerReceiver(wifiScanReceiver, intentFilter);    }

在页面初始化之后就注册广播接收器,在onCreate()中调用。

下面需要配置一下适配器和RecyclerView,在initView()方法中增加如下代码:

wifiAdapter = new WifiAdapter(wifiList);binding.rvWifi.setLayoutManager(new LinearLayoutManager(this));    binding.rvWifi.setAdapter(wifiAdapter);

③ 启动扫描

启动扫描之前要做的是权限的处理,在MainActivity中声明变量:

private ActivityResultLauncher requestPermission;     //请求权限意图

然后在registerIntent()方法中添加如下代码:

requestPermission = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {            if (Boolean.TRUE.equals(result.get(Manifest.permission.NEARBY_WIFI_DEVICES))                    || Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_FINE_LOCATION))) {                //扫描Wifi                showMsg(wifiManager.startScan() ? "扫描Wifi中" : "开启扫描失败");            } else {                showMsg("扫描设备需要此权限");            }        });

最后就是扫描Wifi按钮的点击事件,同样是在initView()方法中添加,代码如下:

//扫描Wifi 按钮点击事件        binding.btnScanWifi.setOnClickListener(v -> {            //是否打开Wifi            if (wifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED) return;            //Android13及以上版本            if (isAndroidTarget(Build.VERSION_CODES.TIRAMISU)) {                if (!hasPermission(Manifest.permission.NEARBY_WIFI_DEVICES) && !hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {                    requestPermission.launch(new String[]{Manifest.permission.NEARBY_WIFI_DEVICES, Manifest.permission.ACCESS_FINE_LOCATION});                    return;                }            } else {                if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {                    requestPermission.launch(new String[]{Manifest.permission.ACCESS_FINE_LOCATION});                    return;                }            }            //扫描Wifi            showMsg(wifiManager.startScan() ? "扫描Wifi中" : "开启扫描失败");        });

这里我在Android 13以上版本同时请求了定位和Wifi权限,如果不这么做的话,调用wifiManager.startScan()就会返回false,而在Android 13以下就只请求定位权限即可,这里还需要给MainActivity添加一个@SuppressLint("MissingPermission")注解,如下图所示:

这样在api 33中使用wifi相关的api时就不会提示错误了,不过你得注意一点,就是你在使用之前确保权限已经获取到,否则会报错闪退。wifiManager.startScan()调用会启动系统扫描,通过系统在扫描结束后,会发出WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的广播,当我们的接收者接收到这个广播的时候,通过WifiManager的getScanResults()就能获取到扫描结果的集合了。如果扫描失败就会返回之前的值,成功最近最新的值。

下面我们运行看一下:

这样看起来还是不错吧,现在有一个问题,就是这个扫描的wifi没有排序,同时没有wifi名称的我们应该过滤掉。

④ 排序与过滤

现在我们已经知道扫描成功和失败的结果区别了,所以就合并以下,同时增加过滤掉空名称的WIFI兵器信号强度进行排序,修改一下广播接收器中的代码,如下所示:

private final BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context c, Intent intent) {            boolean success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);            Log.e(TAG, "onReceive: " + (success ? "成功" : "失败") );            //处理扫描结果            wifiList.clear();            for (ScanResult scanResult : wifiManager.getScanResults()) {                if (!scanResult.SSID.isEmpty()) {                    wifiList.add(scanResult);                }            }            sortByLevel(wifiList);            wifiAdapter.notifyDataSetChanged();        }    };

这里的scanSuccess()和scanFailure()就可以删掉了,再增加一个sortByLevel()方法,代码如下:

private void sortByLevel(List list) {        Collections.sort(list, (lhs, rhs) -> rhs.level - lhs.level);    }

下面我们再运行一下看看:

五、WIFI连接

Wifi的连接相对扫描来说复杂一点,假设现在有三个Wifi,分别是A、B、C。刚开始三个Wifi都没有连接过,在第一次连接A的时候,我们需要输入Wifi密码,密码正确才会建立连接,连接成功后,我们连接B,同样输入密码,此时A就会断开,连接B成功,此时我再转头去连接A,因为之前成功连接过,有保存记录,所以再连接A的时候直接连接就可以了,不再需要密码了。

① Wifi连接工具类

基于这个情况我写了一个工具类,在com.llw.wifi下新建一个EasyWifi类,代码如下所示:

public class EasyWifi {    private static final String TAG = EasyWifi.class.getSimpleName();    private final ConnectivityManager connectivityManager;//连接管理者    private final WifiManager wifiManager;//Wifi管理者    private WifiConnectCallback wifiConnectCallback;    @SuppressLint("StaticFieldLeak")    private static volatile EasyWifi mInstance;    private final Context mContext;    public EasyWifi(Context context) {        mContext = context;        wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);        connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);    }    public static EasyWifi initialize(Context context) {        if (mInstance == null) {            synchronized (EasyWifi.class) {                if (mInstance == null) {                    mInstance = new EasyWifi(context);                }            }        }        return mInstance;    }    public void setWifiConnectCallback(WifiConnectCallback wifiConnectCallback) {        this.wifiConnectCallback = wifiConnectCallback;    }    /**     * 连接Wifi     *     * @param scanResult 扫描结果     * @param password   密码     */    public void connectWifi(ScanResult scanResult, String password) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {            connectByNew(scanResult.SSID, password);        } else {            connectByOld(scanResult, password);        }    }    /**     * Android 10 以下使用     *     * @param scanResult 扫描结果     * @param password   密码     */    private void connectByOld(ScanResult scanResult, String password) {        String ssid = scanResult.SSID;        boolean isSuccess;        WifiConfiguration configured = isExist(ssid);        if (configured != null) {            //在配置表中找到了,直接连接            isSuccess = wifiManager.enableNetwork(configured.networkId, true);        } else {            WifiConfiguration wifiConfig = createWifiConfig(ssid, password, getCipherType(scanResult.capabilities));            int netId = wifiManager.addNetwork(wifiConfig);            isSuccess = wifiManager.enableNetwork(netId, true);        }        Log.d(TAG, "connectWifi: " + (isSuccess ? "成功" : "失败"));    }    /**     * Android 10及以上版本使用此方式连接Wifi     *     * @param ssid     名称     * @param password 密码     */    @SuppressLint("NewApi")    private void connectByNew(String ssid, String password) {        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier.Builder()                .setSsid(ssid)                .setWpa2Passphrase(password)                .build();        //网络请求        NetworkRequest request = new NetworkRequest.Builder()                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)                .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)                .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)                .setNetworkSpecifier(wifiNetworkSpecifier)                .build();        //网络回调处理        ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {            @Override            public void onAvailable(@NonNull Network network) {                super.onAvailable(network);                if (wifiConnectCallback != null) {                    wifiConnectCallback.onSuccess(network);                }            }            @Override            public void onUnavailable() {                super.onUnavailable();                if (wifiConnectCallback != null) {                    wifiConnectCallback.onFailure();                }            }        };        //请求连接网络        connectivityManager.requestNetwork(request, networkCallback);    }    @SuppressLint("NewApi")    private void connectBySug(String ssid, String password) {        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()                .setSsid(ssid)                .setWpa2Passphrase(password)                .setIsAppInteractionRequired(true)                .build();        List suggestionList = new ArrayList<>();        suggestionList.add(suggestion);        int status = wifiManager.addNetworkSuggestions(suggestionList);        if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {            return;        }        IntentFilter intentFilter = new IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);        BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {            @Override            public void onReceive(Context context, Intent intent) {                if (!intent.getAction().equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {                    return;                }            }        };        mContext.registerReceiver(wifiScanReceiver, intentFilter);    }    /**     * 创建Wifi配置     *     * @param ssid     名称     * @param password 密码     * @param type     类型     */    private WifiConfiguration createWifiConfig(String ssid, String password, WifiCapability type) {        WifiConfiguration config = new WifiConfiguration();        config.allowedAuthAlgorithms.clear();        config.allowedGroupCiphers.clear();        config.allowedKeyManagement.clear();        config.allowedPairwiseCiphers.clear();        config.allowedProtocols.clear();        config.SSID = "\"" + ssid + "\"";        WifiConfiguration configured = isExist(ssid);        if (configured != null) {            wifiManager.removeNetwork(configured.networkId);            wifiManager.saveConfiguration();        }        //不需要密码的场景        if (type == WifiCapability.WIFI_CIPHER_NO_PASS) {            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);            //以WEP加密的场景        } else if (type == WifiCapability.WIFI_CIPHER_WEP) {            config.hiddenSSID = true;            config.wepKeys[0] = "\"" + password + "\"";            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);            config.wepTxKeyIndex = 0;            //以WPA加密的场景,自己测试时,发现热点以WPA2建立时,同样可以用这种配置连接        } else if (type == WifiCapability.WIFI_CIPHER_WPA) {            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;    }    /**     * 网络是否连接     */    public static boolean isNetConnected(ConnectivityManager connectivityManager) {        return connectivityManager.getActiveNetwork() != null;    }    /**     * 连接网络类型是否为Wifi     */    public static boolean isWifi(ConnectivityManager connectivityManager) {        if (connectivityManager.getActiveNetwork() == null) {            return false;        }        NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork());        if (networkCapabilities != null) {            return false;        }        return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);    }    /**     * 配置表是否存在对应的Wifi配置     * @param SSID     * @return     */    @SuppressLint("MissingPermission")    private WifiConfiguration isExist(String SSID) {        List existingConfigs = wifiManager.getConfiguredNetworks();        for (WifiConfiguration existingConfig : existingConfigs) {            if (existingConfig.SSID.equals("\"" + SSID + "\"")) {                return existingConfig;            }        }        return null;    }    private WifiCapability getCipherType(String capabilities) {        if (capabilities.contains("WEB")) {            return WifiCapability.WIFI_CIPHER_WEP;        } else if (capabilities.contains("PSK")) {            return WifiCapability.WIFI_CIPHER_WPA;        } else if (capabilities.contains("WPS")) {            return WifiCapability.WIFI_CIPHER_NO_PASS;        } else {            return WifiCapability.WIFI_CIPHER_NO_PASS;        }    }    /**     * wifi连接回调接口     */    public interface WifiConnectCallback {        void onSuccess(Network network);        void onFailure();    }    public enum WifiCapability {        WIFI_CIPHER_WEP, WIFI_CIPHER_WPA, WIFI_CIPHER_NO_PASS    }}

这里对于Wifi的处理,主要是连接方面的,你当然也可以把扫描wifi放进来,对于wifi的连接,需要区分版本进行不同的处理,Android 10 及以上和Android 10以下是不同的方式,下面我们来使用这个工具类。

② 适配器点击处理

下面在WifiAdapter中增加一个接口,代码如下所示:

public interface OnItemClickListener {        void onItemClick(int position);    }

然后提供一个set方法,供使用的地方进行回调处理,代码如下:

public void setOnItemClickListener(OnItemClickListener listener) {        this.listener = listener;    }

然后在onCreateViewHolder()方法中添加接口方法的使用,代码如下:

@NonNull    @Override    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {        ItemWifiRvBinding binding = ItemWifiRvBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);        ViewHolder viewHolder = new ViewHolder(binding);        //添加视图点击事件        binding.getRoot().setOnClickListener(v -> {            if (listener != null) {                listener.onItemClick(viewHolder.getAdapterPosition());            }        });        connectivityManager = (ConnectivityManager) parent.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);        return viewHolder;    }

最后回到MainActivity中进行注册监听

然后实现方法:

@Override    public void onItemClick(int position) {        ScanResult scanResult = wifiList.get(position);        //获取Wifi扫描结果        String capabilities = scanResult.capabilities;        //Wifi状态标识 true:加密,false:开放        boolean wifiStateFlag = capabilities.contains("WEP") || capabilities.contains("PSK") || capabilities.contains("EAP");        if (wifiStateFlag) {                    } else {                    }    }

在这个方法中我们根据是否需要密码进行不同的处理,先看不需要密码的处理,我们这里需要使用工具类,在MainActivity中声明变量:

private EasyWifi easyWifi;

然后在onCreate()方法中进行初始化和设置连接监听。

easyWifi = EasyWifi.initialize(this);    easyWifi.setWifiConnectCallback(this);

然后实现回调方法。

@Override    public void onSuccess(Network network) {        showMsg("连接成功");    }    @Override    public void onFailure() {        showMsg("连接失败");    }

这里我们只是提示一下连接成功和失败。现在就是不需要密码时的处理了,在修改适配器Item点击事件中的if判断,代码如下:

if (wifiStateFlag) {} else {easyWifi.connectWifi(scanResult,"");}

这里不需要密码,而需要密码则麻烦一些,我们需要写一个弹窗来输入密码。

③ 密码弹窗

首先我们需要创建弹窗所需要的布局文件,在layout下新建一个dialog_connect_wifi.xml文件,代码如下所示:

                    com.google.android.material.textfield.TextInputLayout>        androidx.constraintlayout.widget.ConstraintLayout>

这个布局很简单,就一个输入框,两个按钮,下面我们回到MainActivity中,增加如下方法代码:

private void showConnectWifiDialog(ScanResult scanResult) {        BottomSheetDialog dialog = new BottomSheetDialog(this);        DialogConnectWifiBinding binding = DialogConnectWifiBinding.inflate(LayoutInflater.from(this), null, false);        binding.materialToolbar.setTitle(scanResult.SSID);        binding.btnCancel.setOnClickListener(v -> dialog.dismiss());        binding.btnConnect.setOnClickListener(v -> {            String password = binding.etPwd.getText().toString().trim();            if (password.isEmpty()) {                showMsg("请输入密码");                return;            }            easyWifi.connectWifi(scanResult, password);            dialog.dismiss();        });        dialog.setContentView(binding.getRoot());        dialog.show();    }

这个方法就是显示密码输入弹窗,当输入密码之后就连接wifi,连接过程中就会触发之前工具类中的回调,下面我们需要调用这个连接方法,还是之前的那个if语句,代码如下所示:

if (wifiStateFlag) {            showConnectWifiDialog(scanResult);        } else {            easyWifi.connectWifi(scanResult,"");        }

现在可以运行了,因为Wifi连接涉及到隐私信息,所以我就不做动图演示了,连接成功之后会有提示,然后你打开系统Wifi页面会看到如下图所示的:

你会看到这里连接的wifi下面提示了是通过Android13Wifi这个软件进行的wifi连接,当我们的程序被杀死,wifi就会断连,这是因为我们走的不是系统的wifi连接的方式。

六、源码

文章中的wifi使用还是比较浅显的,简单了解一下,而如果你是专门从事WIFI应用开发的话,则需要花心思去研究了,不能流于表面,或者全部靠别人来帮你解决,能帮你的只有自己,山高水长,后会有期~

源码地址 :Android13Wifi

Android WIFI使用简述

2023-03-04

房地产市场从2023年开始分为两半

2023-03-04

世界今日讯!舌状绦虫_关于舌状绦虫的介绍

2023-03-04

全球今日报丨河北平乡:“订单式”职业教育促就业

2023-03-02

13步

2023-03-02

全球看点:系的组词多音字_戏的组词

2023-03-02

全球看点:离岸人民币大涨900点或涨1.3%创近三个月最大涨幅

2023-03-02

2017高考备考指南·文科数学系统复习用书

2023-03-02

天天滚动:mysql和workbench在windows的安装和使用

2023-03-02

【当前热闻】福能东方: 福能东方装备科技股份有限公司第五届董事会第三十三次会议决议公告

2023-03-01

实时:雨衣如何折叠收纳?小学生劳动课上学会实用生活技能

2023-03-01

5年级英语上

2023-03-01

【全球独家】2023华中师范大学第一附属中学后勤集团招聘(厨师+保安+保洁+生活老师等)

2023-03-01

汇川技术:2022年度净利润约42.79亿元 同比增加19.75%_前沿热点

2023-03-01

针式打印机打印出来错位_针式打印机打印错位怎么调整

2023-03-01

公司问答|光明乳业:未授权杭州芯安优选为特殊渠道代理商;长青集团:暂无大规模新建电厂计划

2023-03-01

焦点速递!黑暗沼泽庄园官网在哪下载 最新官方下载安装地址

2023-03-01

宏盛股份董秘回复:截至2023年2月28日,公司股东数为6681

2023-03-01

研究生毕业中级职称评定条件_研究生评中级职称条件

2023-03-01

网友实体店8400买iPhone14Pro Max,评论:被宰了,某东才8100!

2023-03-01

北京朝阳亚运村交通大队治乱疏堵 持续推进地区交通环境治理

2023-03-01

我国累计落实补偿补助资金近270亿元用于长江禁渔退捕渔民安置_天天速读

2023-02-28

JHT酒店-环球焦点

2023-02-28

重庆限行时间和尾号(最新)

2023-02-28

iPhone 15 不会有 6.2 英寸的显示屏

2023-02-28

NOKIA 6230i|全球时快讯

2023-02-28

全球即时看!wps如何vlookup函数_wpsvlookup函数的使用方法

2023-02-28

怎么饲养小香猪_怎样饲养小香猪

2023-02-28

香辣虾的制作方法和教程_香辣虾的制作方法

2023-02-28

大虹桥,彩虹桥|英富曼“蝶变”大虹桥 今年办展超250万平方 环球快看点

2023-02-28

平均年龄超过30岁,国足还要继续啃老?_全球球精选

2023-02-28

我在闲鱼上卖了一辆奥迪A6,五分钟成交客户2千公里打飞机来提车|视点

2023-02-28

吊车吊东西口诀吊车吊装比力表_吊车吊东西口诀_每日快播

2023-02-28

两个月逾50家公司推股权激励计划|环球聚焦

2023-02-28

热点在线丨中国车企助力北海道绿色转型

2023-02-27

美军使用34辆油罐车盗取叙利亚石油-天天热闻

2023-02-27

八大处整形价目表去疤_八大处整形价目表 当前快播

2023-02-27

新资讯:狂飙33+4+5,带队豪取14连胜!悄悄崛起成太阳克星,你才是二当家

2023-02-27

港股概念追踪 | 煤炭股逆市走强!行业基本面短期偏紧 价格仍有上行驱动 板块或进入反弹阶段(附概念股)_全球热头条

2023-02-27

pushbt6平台-推送手机版_pushbt6

2023-02-27

73岁大娘扛百斤水泥赚钱,当地领导称老人常自愿劳动,大家怎么看?

2023-02-27

长沙自贸区落户到浏阳需要多久时间

2023-02-27

现充是褒义还是贬义_现充

2023-02-27

黑索金

2023-02-27

当前观点:阜南嗨子戏《三击掌》亮相央视舞台戏曲频道

2023-02-27

羽组词有哪些词语的组词_每日消息

2023-02-27

不妖城-世界新资讯

2023-02-27

怎样判断蘑菇是否新鲜 如何判断蘑菇是否新鲜

2023-02-27

“湘”当自觉自信自强|登上“神舟十四号”的太空椒,映红湘阴富民大产业 当前速讯

2023-02-26

残暴狮

2023-02-26

环球通讯!办事更方便!新街口设立西城区首个收发件政务服务自助系统

2023-02-26

【全球速看料】挺进深远海!中国首艘双模式高速风电运维船顺利出海

2023-02-26

死亡歌颂者 全球消息

2023-02-26

大熊猫“乐乐”死因已初步确定,中方已为接返大熊猫“丫丫”做好准备

2023-02-26

化学专业考研方向及就业前景_化学专业考研方向 全球头条

2023-02-26

电水龙头 当前播报

2023-02-26

合同起诉有效期多久

2023-02-26

中国探索地理:三国探秘之旅_当前热门

2023-02-25

全球热消息:源:人类文明中华源流考_关于源:人类文明中华源流考介绍

2023-02-25

周小川:未来国家统筹养老金需个人账户加以配合才能达到满意水平

2023-02-25

别说抱歉 别说再见 电影-当前热门

2023-02-25

威马再发内部信:部分人员先行复工复产 其余人无薪休假

2023-02-25

【园区企业】阿依瓦荣登工信部“2022 年工业互联网 APP 优秀解决方案名单”_当前讯息

2023-02-25

怏怏不乐|全球观点

2023-02-25

天坛医院的图片_天坛医院平面图 全球热资讯

2023-02-25

玉米面条的做法

2023-02-25

全球热议:婆婆当着儿子的面打儿媳,儿媳只顾护孩子没还手,事后把家砸了

2023-02-25

“床底有3物,家散人不旺”,无论床有多宽敞,床底也不放3种物品-全球头条

2023-02-25

天天速看:哺乳期能吃哈密瓜?_哺乳期可以吃哈密瓜么

2023-02-24

逸彩新世界

2023-02-24

快看点丨顺威股份(002676.SZ):顺威新能源拟参设穗开智境基金 以投资超境控股

2023-02-24

2023金华哪些企业能申请女职工产假期间社会保险补贴?

2023-02-24

云医院|全球快讯

2023-02-24

魔兽真三国无双武器_魔兽真三国无双ai

2023-02-24

播报:2月23日深圳新房住宅网签95套 二手房住宅网签166套

2023-02-24

每日看点!02月24日10时陕西宝鸡昨日累计报告阳性感染者确诊261例 怎么判断自己是否属于轻型感染者

2023-02-24

【播资讯】Linus Torvalds 怒怼:不要提交没有注释的请求

2023-02-24

环球消息!抗原自测板块2月23日跌0.82%,华盛昌领跌,主力资金净流出3.58亿元

2023-02-24

环球动态:it软件培训机构排名_山东it培训机构排名榜

2023-02-23

巧了!民警喝奶茶顺手抓了个嫌疑人-当前消息

2023-02-23

吉林出台42条实施意见建设农业强省|每日焦点

2023-02-23

环球热资讯!铂力特: 西安铂力特增材技术股份有限公司2022年度业绩快报公告

2023-02-23

北斗“车道级导航”将普及全国_看点

2023-02-23

高学历低龄老人线上求职碰壁具体详细内容是什么 天天观热点

2023-02-23

永州零陵:送立功喜报前 区委书记为二等功臣披红戴花

2023-02-23

【聚看点】市委平安北京建设领导小组全体(扩大)会议召开 尹力主持 刘伟出席

2023-02-23

议论文五段三论点范文(共6篇)

2023-02-23

【新视野】入境游复苏!贵州迎来首个香港入境旅游团

2023-02-22

百度Q4及全年财报:季度经营利润及利润率双增长,正全力冲刺文心一言

2023-02-22

全球微头条丨酷狗怎么搜索别人的id

2023-02-22

3月1日起 仲恺高新区这202个道路临时泊位将收费

2023-02-22

旧物改造手工制作大全视频_旧物改造手工制作大全

2023-02-22

天天新资讯:小孩取名字大全2023属兔免费 兔宝宝取名免费大全

2023-02-22

诺如病毒感染防控知多少_新资讯

2023-02-22

天天百事通!双桥办事处观悦社区开展“公益律师进社区、法律服务在身边”活动

2023-02-22

中泰证券:2月21日获融资买入832.34万元,占当日流入资金比例11.18%_最新

2023-02-22

有什么迪士尼公主电影_迪士尼公主电影有哪些-天天精选

2023-02-22

全球播报:45岁曹颖高调秀幸福!昔日“霍水仙”风采依旧,赢过董卿传奇犹在

2023-02-21

天天微资讯!向网球看齐,乒乓球也打算酝酿大满贯赛,但未来世界冠军不好拿

2023-02-21

裴怎么读拼音怎么写_裴怎么读 天天信息

2023-02-21