11、WiFi热点创建(传统AP模式):WifiManager.startLocalOnlyHotspot()、OnStartLocalOnlyHotspotCallback、获取热点SSID与密码
好,咱们今天聊聊WiFi热点创建。说白了,就是把手机变成一个“小路由器”,让其他设备连上来。
Android里创建热点,传统方式有两种:一种是系统API,另一种是反射调用隐藏API。我个人更推荐用系统API,也就是startLocalOnlyHotspot()。为什么?因为它安全、稳定,而且Google官方一直在维护。
11.1 传统AP模式 vs 其他模式
先理清概念。传统AP模式,就是手机作为接入点,其他设备作为客户端。跟WiFi Direct、WiFi Aware不一样,AP模式是一对多的中心化结构。
我记得刚做WiFi开发时,总把AP模式和热点模式搞混。其实它们是一回事——都是让设备充当无线接入点。
| 模式 | 角色 | 典型场景 |
|---|---|---|
| 传统AP模式 | 手机作为热点 | 共享网络、临时组网 |
| WiFi Direct | 点对点连接 | 文件传输、投屏 |
| WiFi Aware | 邻居发现 | 附近设备服务发现 |
11.2 startLocalOnlyHotspot() 核心API
这个方法是Android 8.0(API 26)引入的。它最大的好处是——不需要系统权限。你想想看,以前创建热点得申请SYSTEM_ALERT_WINDOW或者root权限,现在直接调就行。
但有个坑:热点名称和密码由系统自动生成,你不能自己指定。嗯,这里要注意,很多新手上来就想设个“MyHotspot”之类的名字,结果发现改不了。
核心方法签名:
public void startLocalOnlyHotspot(
@NonNull OnStartLocalOnlyHotspotCallback callback,
@Nullable Handler handler
)
11.3 OnStartLocalOnlyHotspotCallback 回调
这个回调接口有三个方法,我一个个说。
private final WifiManager.OnStartLocalOnlyHotspotCallback callback =
new WifiManager.OnStartLocalOnlyHotspotCallback() {
@Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
// 热点创建成功
WifiConfiguration config = reservation.getWifiConfiguration();
String ssid = config.SSID;
String password = config.preSharedKey;
Log.d("Hotspot", "SSID: " + ssid + ", Password: " + password);
}
@Override
public void onStopped() {
// 热点已关闭
Log.d("Hotspot", "热点已停止");
}
@Override
public void onFailed(int reason) {
// 创建失败
Log.e("Hotspot", "热点创建失败,原因码: " + reason);
}
};
我在项目中遇到过一个问题:onFailed返回的原因码,文档里只说了几个常量。比如WifiManager.START_HOTSPOT_FAILED_GENERIC(通用错误)、WifiManager.START_HOTSPOT_FAILED_NO_CHANNEL(无可用信道)。但实际测试时,有些厂商会返回自定义的错误码,这时候就得靠经验去排查了。
11.4 获取热点SSID与密码
热点创建成功后,系统会返回一个LocalOnlyHotspotReservation对象。这个对象里藏着热点的配置信息。
获取方式:
WifiConfiguration config = reservation.getWifiConfiguration();
String ssid = config.SSID; // 注意:SSID带双引号
String password = config.preSharedKey; // 密码也带双引号
记得把双引号去掉,不然其他设备连的时候会报错。
我曾经踩过这个坑——直接把config.SSID显示到界面上,结果用户说“怎么我的热点名字带引号?”后来才意识到,WifiConfiguration里的字符串是带引号的,需要自己处理一下。
// 去掉双引号的工具方法
private String stripQuotes(String str) {
if (str != null && str.startsWith("\"") && str.endsWith("\"")) {
return str.substring(1, str.length() - 1);
}
return str;
}
11.5 完整代码示例
下面是一个完整的启动热点示例。我习惯把逻辑封装成一个工具类,方便复用。
public class HotspotManager {
private WifiManager wifiManager;
private WifiManager.LocalOnlyHotspotReservation reservation;
public HotspotManager(Context context) {
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
}
public void startHotspot() {
if (wifiManager == null) return;
wifiManager.startLocalOnlyHotspot(
new WifiManager.OnStartLocalOnlyHotspotCallback() {
@Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation res) {
reservation = res;
WifiConfiguration config = res.getWifiConfiguration();
String ssid = stripQuotes(config.SSID);
String password = stripQuotes(config.preSharedKey);
// 这里可以回调给UI层显示
}
@Override
public void onStopped() {
reservation = null;
}
@Override
public void onFailed(int reason) {
// 处理失败
}
}, null);
}
public void stopHotspot() {
if (reservation != null) {
reservation.close();
reservation = null;
}
}
private String stripQuotes(String str) {
if (str != null && str.startsWith("\"") && str.endsWith("\"")) {
return str.substring(1, str.length() - 1);
}
return str;
}
}
11.6 避坑指南
做热点开发,有几个坑我不得不提。
- 热点与WiFi连接冲突:开启热点时,手机会自动断开当前WiFi连接。这是系统行为,你没法控制。
- Android 10以上限制:从Android 10开始,热点创建需要位置权限。不然
startLocalOnlyHotspot()会直接失败。 - 厂商定制问题:有些国产手机对热点功能做了修改,比如小米、华为。我遇到过华为手机返回的SSID是乱码的情况,后来发现是编码问题。
重要提醒:
热点创建成功后,一定要在合适的时机调用reservation.close()关闭热点。否则热点会一直开着,耗电又占资源。我建议在Activity的onDestroy()或Fragment的onDestroyView()里关闭。
11.7 知识体系图
下面这张图,把热点创建的整个流程串起来了。你看一眼就能明白。
11.8 总结
热点创建其实不复杂,核心就三步:调API、等回调、拿配置。但细节决定成败——权限、引号、厂商适配,哪个不注意都会翻车。
我个人建议,在实际项目中,最好封装一个热点管理类,把启动、停止、状态监听都包进去。这样代码干净,也好维护。
好了,这一章就到这儿。记住:热点不是你想开,想开就能开——得看系统给不给你信道,给不给你权限。