3、WiFi权限声明:AndroidManifest中WiFi相关权限
做WiFi开发,第一关就是权限。我见过太多新手,代码写得飞起,结果一运行就崩——哦,忘了加权限。这玩意儿说简单也简单,说坑也真坑。尤其是Android 10之后,规则变了好几次,稍不注意就踩雷。
3.1 静态权限:AndroidManifest里必须写的
先看最基础的。无论你的App跑在哪个Android版本,下面这几个权限声明是跑不掉的:
<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.ACCESS_COARSE_LOCATION" />
简单解释一下:
- ACCESS_WIFI_STATE:读取WiFi状态。比如当前连没连上、信号强度多少、SSID是什么。这个权限是普通权限,系统自动授予,不需要用户弹窗同意。
- CHANGE_WIFI_STATE:修改WiFi状态。比如打开/关闭WiFi、连接某个热点。也是普通权限。
- ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION:这两个是危险权限。为什么WiFi要定位权限?因为Android系统认为,扫描WiFi热点可以推断出你的位置。说白了,Google把WiFi扫描和定位绑死了。
⚠️ 注意:从Android 8.0(API 26)开始,如果你只扫描WiFi但不获取位置,系统仍然要求你声明定位权限。否则
startScan()返回的结果列表永远是空的。我当年第一次踩这个坑时,排查了整整一个下午。
3.2 运行时权限:Android 6.0+ 必须动态申请
从Android 6.0(API 23)开始,危险权限不能只写在Manifest里了。你得在代码里弹窗让用户同意。WiFi相关的定位权限就是典型的危险权限。
我个人习惯这样写:
// 检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// 请求权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_CODE_LOCATION);
} else {
// 已有权限,开始扫描
startWifiScan();
}
然后在回调里处理结果:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_LOCATION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startWifiScan();
} else {
// 用户拒绝了,你可以提示或者引导去设置
Toast.makeText(this, "需要定位权限才能扫描WiFi", Toast.LENGTH_SHORT).show();
}
}
}
💡 小技巧:如果用户点了「拒绝并不再询问」,下次再弹窗就没用了。我建议在这种情况下,直接跳转到App的设置页面,让用户手动开启。用
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)就能做到。
3.3 Android 10+ 的重大变化:精确定位 vs 模糊定位
嗯,这里要注意。Android 10(API 29)引入了一个新概念:精确定位和模糊定位。
以前你只要声明ACCESS_FINE_LOCATION就够了。但现在,Google希望用户有更多选择权:
- ACCESS_FINE_LOCATION:精确定位。能获取到WiFi的详细扫描结果,包括SSID、BSSID、信号强度等。
- ACCESS_COARSE_LOCATION:模糊定位。只能知道大概位置,WiFi扫描结果会被阉割——你拿不到具体的SSID和BSSID。
为什么会这样?说白了,Google在隐私保护上越来越严。如果你的App只是想知道「用户是不是在家附近」,那模糊定位就够了,没必要拿精确位置。
我建议的做法是:
// 同时请求两个权限,让用户选择
String[] permissions = new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
};
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_LOCATION);
然后在代码里判断用户给了哪个:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
// 精确定位,可以获取完整WiFi信息
} else if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
// 模糊定位,只能获取有限信息
} else {
// 没有权限,提示用户
}
📌 核心要点:如果你要做WiFi扫描并显示SSID名称,必须申请
ACCESS_FINE_LOCATION。模糊定位拿不到这些数据。我曾经有个项目,测试时发现某些手机上WiFi列表全是「未知网络」,排查半天才发现是用户只给了模糊定位。
3.4 Android 12+ 的进一步收紧
Android 12(API 31)又加了一条:ACCESS_FINE_LOCATION现在需要用户在系统设置里单独开启「精确位置」开关。也就是说,即使你申请了精确定位权限,用户也可以在设置里把它降级为模糊定位。
这意味着什么?你的App必须做好两手准备:
- 检查是否有
ACCESS_FINE_LOCATION权限 - 如果没有,检查是否有
ACCESS_COARSE_LOCATION权限 - 如果都没有,引导用户去设置开启
我个人习惯用一个工具方法来判断:
public static boolean hasLocationPermission(Context context) {
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
}
public static boolean hasCoarseLocationPermission(Context context) {
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
}
3.5 一张图看懂权限体系
下面这张图总结了WiFi权限的层级关系,你一看就明白:
3.6 避坑指南:我踩过的那些坑
做WiFi开发这几年,我总结了几条血泪教训:
- 别在Android 6.0以下设备上动态申请权限——系统会直接崩溃。记得先判断
Build.VERSION.SDK_INT。 - 用户拒绝权限后不要一直弹窗——用户体验极差。我建议拒绝两次后,直接引导去设置页。
- Android 10以上,只声明ACCESS_COARSE_LOCATION是拿不到SSID的——别问我怎么知道的,问就是踩过坑。
- 测试时记得用真机——模拟器上WiFi权限行为有时和真机不一样,尤其是定位权限。
⚠️ 重要提醒:从Android 12开始,如果你在Manifest里声明了
ACCESS_FINE_LOCATION,但用户在实际使用中只给了模糊定位,你的checkSelfPermission返回的仍然是PERMISSION_GRANTED。你需要额外调用LocationManager.isLocationEnabled()来判断精确位置是否开启。
3.7 总结一下
WiFi权限这块,说白了就是三件事:
- Manifest里写全——普通权限和危险权限都写上,别漏。
- 运行时动态申请——危险权限必须弹窗让用户同意。
- 适配不同版本——Android 10关注精确定位,Android 12关注精确开关。
把这些搞定了,你的WiFi功能就成功了一半。剩下的就是业务逻辑了。
公众号:蓝海资料掘金营,微信deep3321