You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Android Oreo系统中编程创建带SSID和密码的WiFi热点?

搞定Android Oreo及以上创建WiFi热点返回false的问题

为啥旧代码在Oreo上不好使了?

从Android 8.0(API 26)开始,谷歌收紧了对WiFi热点配置的管控,旧的反射调用setWifiApEnabled的路子在普通应用里会直接返回false——一方面是系统限制了非系统应用修改热点配置的权限,另一方面旧API的内部实现也悄悄变了。

一步步解决问题

1. 先把权限配全

首先得在AndroidManifest.xml里加上必要的权限,少一个都可能翻车:

<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" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- Android 10及以上还要加这个 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

划重点:WRITE_SETTINGS权限不能只在Manifest里写,得动态申请!先调用Settings.System.canWrite(context)检查有没有授权,没授权的话要引导用户跳转到系统设置页面开启。

2. 适配Oreo的反射代码

针对Oreo及以上系统,得调整反射调用的逻辑,旧方法不好使了,试试下面的代码:

private boolean createCustomHotspot(WifiManager wifiManager, String ssid, String password) {
    // 先构建热点配置
    WifiConfiguration config = new WifiConfiguration();
    config.SSID = ssid;
    config.preSharedKey = password;
    config.hiddenSSID = false;
    config.status = WifiConfiguration.Status.ENABLED;
    // 配置WPA2-PSK相关参数
    config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
    config.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
    config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
    config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
    config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
    config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
    config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);

    try {
        // 先关掉已有的热点,避免冲突
        Method getApConfigMethod = wifiManager.getClass().getMethod("getWifiApConfiguration");
        WifiConfiguration existingConfig = (WifiConfiguration) getApConfigMethod.invoke(wifiManager);
        if (existingConfig != null) {
            Method disableApMethod = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
            disableApMethod.invoke(wifiManager, existingConfig, false);
        }

        // 尝试用旧方法设置新热点
        Method setApMethod = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
        boolean result = (boolean) setApMethod.invoke(wifiManager, config, true);
        return result;
    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
        // 旧方法不行的话,试试Oreo可能支持的另一种反射方式
        try {
            Method startSoftApMethod = wifiManager.getClass().getMethod("startSoftAp", WifiConfiguration.class);
            startSoftApMethod.invoke(wifiManager, config);
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }
}

3. Android 10及以上用官方API更靠谱

如果你的应用目标是Android 10(API 29)及以上,直接用官方提供的startSoftAp相关API,不用折腾反射:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    // 构建热点规格
    WifiNetworkSpecifier.Builder specBuilder = new WifiNetworkSpecifier.Builder();
    specBuilder.setSsid(ssid);
    specBuilder.setWpa2Passphrase(password);

    // 创建网络请求
    NetworkRequest request = new NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .setNetworkSpecifier(specBuilder.build())
            .build();

    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    // 请求创建热点
    connectivityManager.requestNetwork(request, new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            super.onAvailable(network);
            // 热点创建成功啦
        }

        @Override
        public void onUnavailable() {
            super.onUnavailable();
            // 唉,创建失败了
        }
    });
}

注意:这种方式创建的是临时热点,应用退出或者断开网络后会自动关闭。

最后提几个坑

  • 厂商限制:有些Oreo及以上的设备,就算你权限全拿了,普通应用还是没法创建自定义热点——因为厂商加了额外限制,这种情况下只能引导用户手动去系统设置里创建热点。
  • 设备兼容性:不同厂商的ROM对热点API的实现不一样,记得多拿几个设备测试哦~

内容的提问来源于stack exchange,提问作者shyam002

火山引擎 最新活动