最近在写一个小工具,需要判断用户是否开启了便携式热点(Wi-Fi 热点),发现 Android 系统并没有提供直接的公开 API 来判断热点状态,遂调研了一下实现方案并记录。

WifiManager 反射调用

虽然 Android 没有提供公开 API 来判断热点状态,但可以通过反射调用 WifiManager 的隐藏方法来实现。

fun isHotspotEnabled(context: Context): Boolean {
    val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
    return try {
        val method: Method = wifiManager.javaClass.getDeclaredMethod("isWifiApEnabled")
        method.isAccessible = true
        method.invoke(wifiManager) as Boolean
    } catch (e: Exception) {
        e.printStackTrace()
        false
    }
}

isWifiApEnabled 被标记为 @hide,它内部是调用 getWifiApState 方法来判断热点状态的。

@SystemService(Context.WIFI_SERVICE)
public class WifiManager {
    ...
    /**
     * Return whether tethered Wi-Fi AP is enabled or disabled.
     * @return {@code true} if tethered  Wi-Fi AP is enabled
     * @see #getWifiApState()
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
    public boolean isWifiApEnabled() {
        return getWifiApState() == WIFI_AP_STATE_ENABLED;
    }
}

如果我们需要区分热点状态值,也可以通过反射调用 getWifiApState 方法来获取,返回值为 int 类型,定义如下:

@SystemService(Context.WIFI_SERVICE)
public class WifiManager {
    ...
    @SystemApi
    public static final int WIFI_AP_STATE_DISABLING = 10;
    @SystemApi
    public static final int WIFI_AP_STATE_DISABLED = 11;
    @SystemApi
    public static final int WIFI_AP_STATE_ENABLING = 12;
    @SystemApi
    public static final int WIFI_AP_STATE_ENABLED = 13;
    @SystemApi
    public static final int WIFI_AP_STATE_FAILED = 14;
}

望文生义,不用过多解释了。

TetheringManager 监听

Android 16 TetheringManager 提供了更规范的 API 来监控热点状态。

@RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
@RequiresApi(Build.VERSION_CODES.BAKLAVA)
fun registerTetheringCallback(context: Context, onHotspotStateChanged: (Boolean) -> Unit) {
    val tetheringManager = context.getSystemService(Context.TETHERING_SERVICE) as TetheringManager
    tetheringManager.registerTetheringEventCallback(
        Runnable::run,
        object : TetheringManager.TetheringEventCallback {
            override fun onTetheredInterfacesChanged(interfaces: MutableSet<TetheringInterface>) {
                onHotspotStateChanged(interfaces.isNotEmpty())
            }
        }
    )
}

它相比上面的反射调用更加灵活,但需要更新的设备支持,目前来说应用较少。