Android打造易用的 WiFi 工具类:WifiUtils 封装实践

Android在全局范围内使用WifiUtils工具类,我们可以将其设计为一个单例,并通过Application类进行初始化。这样可以确保在整个应用程序中只有一个WifiUtils实例,并且可以在任何地方访问它。以下是实现全局使用的步骤和代码示例:

记得在AndroidManifest.xml配置文件中配置权限:

复制代码
<!--    网络权限-->
    <uses-permission android:name="android.permission.INTERNET"/>
<!--    wifi权限-->
    <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_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>

1. 创建自定义Application类

首先,创建一个自定义的Application类,用于初始化WifiUtils。

复制代码
import android.app.Application;

public class MyApplication extends Application {

    private static MyApplication instance;
    private WifiUtils wifiUtils;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        wifiUtils = WifiUtils.getInstance(this);
    }

    public static MyApplication getInstance() {
        return instance;
    }

    public WifiUtils getWifiUtils() {
        return wifiUtils;
    }
}

  1. 在AndroidManifest.xml中注册Application类

在AndroidManifest.xml中,将MyApplication类设置为应用程序的Application类。

复制代码
 <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyPagerViewDemo"
        tools:targetApi="31">
    ...
</application>

3. 修改WifiUtils工具类

确保WifiUtils是一个单例类,并且可以通过Application类访问。

复制代码
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkSuggestion;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

public class WifiUtils {

    private static final String TAG = "WifiUtils";
    private static final int CONNECTION_TIMEOUT = 30000; // 30秒超时
    private static final int MAX_RETRY_COUNT = 3; // 最大重试次数

    private static WifiUtils instance;
    private WifiManager wifiManager;
    private Context context;
    private Handler mainHandler;
    private Set<ScanResult> cachedScanResults;
    private WifiStateReceiver wifiStateReceiver;
    private AtomicBoolean isConnecting = new AtomicBoolean(false);
    private Runnable connectionTimeoutTask;
    private int retryCount = 0;

    // 回调接口
    public interface WifiConnectionListener {
        void onConnectionSuccess();
        void onConnectionFailure(String errorMessage);
    }

    public interface WifiScanListener {
        void onScanResultsReceived(List<ScanResult> scanResults);
        void onScanFailure(String errorMessage);
    }

    // 单例模式
    public static synchronized WifiUtils getInstance(Context context) {
        if (instance == null) {
            instance = new WifiUtils(context.getApplicationContext());
        }
        return instance;
    }

    private WifiUtils(Context context) {
        this.context = context;
        this.wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        this.mainHandler = new Handler(Looper.getMainLooper());
        this.cachedScanResults = new HashSet<>();
        registerWifiStateReceiver();
    }

    // 注册WiFi状态监听
    private void registerWifiStateReceiver() {
        wifiStateReceiver = new WifiStateReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        context.registerReceiver(wifiStateReceiver, filter);
    }

    // 注销WiFi状态监听
    public void unregisterWifiStateReceiver() {
        if (wifiStateReceiver != null) {
            context.unregisterReceiver(wifiStateReceiver);
            wifiStateReceiver = null;
        }
    }

    // 检查WiFi是否启用
    public boolean isWifiEnabled() {
        return wifiManager != null && wifiManager.isWifiEnabled();
    }

    // 启用WiFi
    public boolean enableWifi() {
        if (wifiManager != null) {
            return wifiManager.setWifiEnabled(true);
        }
        return false;
    }

    // 扫描WiFi
    public void startScan(@NonNull WifiScanListener listener) {
        if (wifiManager == null) {
            listener.onScanFailure("WifiManager is not available");
            return;
        }

        new Thread(() -> {
            boolean scanSuccess = wifiManager.startScan();
            if (!scanSuccess) {
                mainHandler.post(() -> listener.onScanFailure("Failed to start scan"));
            }
        }).start();
    }

    // 获取扫描结果
    public List<ScanResult> getScanResults() {
        return new ArrayList<>(cachedScanResults);
    }

    // 连接到指定WiFi
    public void connectToWifi(@NonNull String ssid, @NonNull String password, WifiConnectionListener listener) {
        if (isConnecting.get()) {
            listener.onConnectionFailure("A connection attempt is already in progress");
            return;
        }

        if (TextUtils.isEmpty(ssid) || TextUtils.isEmpty(password)) {
            listener.onConnectionFailure("SSID or password is empty");
            return;
        }

        if (!isWifiEnabled()) {
            listener.onConnectionFailure("WiFi is not enabled");
            return;
        }

        isConnecting.set(true);
        startConnectionTimeout(listener);

        new Thread(() -> {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                connectToWifiWithSuggestion(ssid, password, listener);
            } else {
                connectToWifiLegacy(ssid, password, listener);
            }
        }).start();
    }

    // 传统方式连接WiFi(Android 9及以下)
    private void connectToWifiLegacy(String ssid, String password, WifiConnectionListener listener) {
        WifiConfiguration config = createWifiConfig(ssid, password);
        if (config == null) {
            handleConnectionFailure(listener, "Failed to create WifiConfiguration");
            return;
        }

        int netId = wifiManager.addNetwork(config);
        if (netId == -1) {
            handleConnectionFailure(listener, "Failed to add network");
            return;
        }

        boolean isDisconnected = wifiManager.disconnect();
        if (!isDisconnected) {
            handleConnectionFailure(listener, "Failed to disconnect from current network");
            return;
        }

        boolean isEnabled = wifiManager.enableNetwork(netId, true);
        if (!isEnabled) {
            handleConnectionFailure(listener, "Failed to enable network");
            return;
        }

        boolean isReconnected = wifiManager.reconnect();
        if (!isReconnected) {
            handleConnectionFailure(listener, "Failed to reconnect");
            return;
        }

        handleConnectionSuccess(listener);
    }

    // 使用WifiNetworkSuggestion连接WiFi(Android 10及以上)
    @RequiresApi(api = Build.VERSION_CODES.Q)
    private void connectToWifiWithSuggestion(String ssid, String password, WifiConnectionListener listener) {
        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
                .setSsid(ssid)
                .setWpa2Passphrase(password)
                .setIsHiddenSsid(false)
                .build();

        List<WifiNetworkSuggestion> suggestionsList = new ArrayList<>();
        suggestionsList.add(suggestion);

        int status = wifiManager.addNetworkSuggestions(suggestionsList);
        if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
            handleConnectionSuccess(listener);
        } else {
            handleConnectionFailure(listener, "Failed to add network suggestion");
        }
    }

    // 处理连接成功
    private void handleConnectionSuccess(WifiConnectionListener listener) {
        isConnecting.set(false);
        retryCount = 0;
        mainHandler.removeCallbacks(connectionTimeoutTask);
        mainHandler.post(listener::onConnectionSuccess);
    }

    // 处理连接失败
    private void handleConnectionFailure(WifiConnectionListener listener, String errorMessage) {
        if (retryCount < MAX_RETRY_COUNT) {
            retryCount++;
            Log.d(TAG, "Retrying connection attempt " + retryCount);
            connectToWifi(ssid, password, listener);
        } else {
            isConnecting.set(false);
            retryCount = 0;
            mainHandler.removeCallbacks(connectionTimeoutTask);
            mainHandler.post(() -> listener.onConnectionFailure(errorMessage));
        }
    }

    // 启动连接超时任务
    private void startConnectionTimeout(WifiConnectionListener listener) {
        connectionTimeoutTask = () -> handleConnectionFailure(listener, "Connection timed out");
        mainHandler.postDelayed(connectionTimeoutTask, CONNECTION_TIMEOUT);
    }

    // 创建WifiConfiguration
    private WifiConfiguration createWifiConfig(String ssid, String password) {
        try {
            WifiConfiguration config = new WifiConfiguration();
            config.SSID = String.format("\"%s\"", ssid);
            config.preSharedKey = String.format("\"%s\"", password);
            config.status = WifiConfiguration.Status.ENABLED;
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
            config.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
            return config;
        } catch (Exception e) {
            Log.e(TAG, "Failed to create WifiConfiguration: " + e.getMessage());
            return null;
        }
    }

    // 获取当前连接的WiFi信息
    public WifiInfo getConnectedWifiInfo() {
        if (wifiManager != null) {
            return wifiManager.getConnectionInfo();
        }
        return null;
    }

    // 断开当前WiFi连接
    public boolean disconnectFromWifi() {
        if (wifiManager != null) {
            return wifiManager.disconnect();
        }
        return false;
    }

    // WiFi状态监听器
    private class WifiStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
                // 更新缓存扫描结果
                if (wifiManager != null) {
                    cachedScanResults = new HashSet<>(wifiManager.getScanResults());
                }
            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) {
                // 处理网络状态变化
                WifiInfo wifiInfo = getConnectedWifiInfo();
                if (wifiInfo != null) {
                    Log.d(TAG, "Connected to: " + wifiInfo.getSSID());
                }
            }
        }
    }
}

4. 在全局范围内使用WifiUtils

在任何Activity或Fragment中,都可以通过MyApplication类获取WifiUtils实例,并调用其方法。

复制代码
public class MainActivity extends AppCompatActivity {

    private WifiUtils wifiUtils;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取WifiUtils实例
        wifiUtils = ((MyApplication) getApplication()).getWifiUtils();

        // 检查并启用WiFi
        if (!wifiUtils.isWifiEnabled()) {
            wifiUtils.enableWifi();
        }

        // 扫描WiFi
        wifiUtils.startScan(new WifiUtils.WifiScanListener() {
            @Override
            public void onScanResultsReceived(List<ScanResult> scanResults) {
                for (ScanResult result : scanResults) {
                    Log.d("WifiScan", "SSID: " + result.SSID);
                }
            }

            @Override
            public void onScanFailure(String errorMessage) {
                Log.e("WifiScan", "Scan failed: " + errorMessage);
            }
        });

        // 连接到指定WiFi
        String ssid = "YourWiFiSSID";
        String password = "YourWiFiPassword";
        wifiUtils.connectToWifi(ssid, password, new WifiUtils.WifiConnectionListener() {
            @Override
            public void onConnectionSuccess() {
                Log.d("WifiConnect", "Connected to " + ssid);
            }

            @Override
            public void onConnectionFailure(String errorMessage) {
                Log.e("WifiConnect", "Failed to connect: " + errorMessage);
            }
        });

        // 获取当前连接的WiFi信息
        WifiInfo wifiInfo = wifiUtils.getConnectedWifiInfo();
        if (wifiInfo != null) {
            Log.d("WifiInfo", "Connected to: " + wifiInfo.getSSID());
        }

        // 断开当前WiFi连接
        boolean isDisconnected = wifiUtils.disconnectFromWifi();
        if (isDisconnected) {
            Log.d("WifiDisconnect", "Disconnected from current WiFi");
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        wifiUtils.unregisterWifiStateReceiver();
    }
}

5. 总结

通过以上步骤,我们实现了WifiUtils工具类的全局使用。它具有以下优点:

  1. 单例模式:确保全局只有一个实例。
  2. 全局访问 :通过Application类在任何地方访问WifiUtils。
  3. 高性能:后台线程执行耗时操作,避免阻塞主线程。
  4. 健壮性:详细的错误处理和状态检查。
  5. 易用性:模块化设计,接口清晰,易于扩展和维护。

在实际开发中,建议根据具体需求进一步调整和扩展功能。

相关推荐
xcl09255 小时前
北京24小时自助健身房系统软件开发实战:从架构到部署全流程指南
java·spring boot·架构
一 乐6 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
卓怡学长7 小时前
w214基于jsp知道特产网
java·intellij-idea
步行cgn7 小时前
Spring 注解使用详解
java·spring
其实防守也摸鱼7 小时前
Fastjson 反序列化漏洞(JNDI注入)
android·数据库·安全·oracle·自动化·fastjson·反序列化
一条小小yu7 小时前
Spring IoC的理解
java·后端·spring
落魄实习生7 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈7 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
狗凯之家源码网8 小时前
微博图床 PHP 系统搭建与实战应用指南
android·开发语言·php