Kotlin中筛选具备通知权限的第三方应用失败,权限检查逻辑异常求助
Kotlin中筛选具备通知权限的第三方应用失败,权限检查逻辑异常求助
看起来你在筛选有通知权限的应用时踩了几个版本适配和API调用的坑,我来帮你梳理下问题所在和修正方案:
核心问题分析
你当前的权限检查逻辑有两个关键错误:
- 传错了UID参数:在调用AppOps相关方法时,你用了
android.os.Process.myUid()(当前APP的UID),但我们要检查的是目标应用的UID——AppOps是基于应用唯一UID记录权限状态的,用自己的UID查其他应用的权限,结果必然错误,这就是你拿到MODE_IGNORED(1)而非预期MODE_ALLOWED(0)的根本原因。 - 版本适配不完整:不同Android版本检查其他应用通知权限的官方API差异很大,尤其是Android 13(API 33)之后,官方提供了更直接的查询方式,无需再绕AppOps。
修正后的完整代码
下面是修复后的NotificationPermissionHelper类,包含了正确的权限检查逻辑和版本适配:
class NotificationPermissionHelper(private val context: Context) { fun getAllApps(): List<ApplicationInfo> { val packageManager = context.packageManager val mainIntent = Intent(Intent.ACTION_MAIN, null) mainIntent.addCategory(Intent.CATEGORY_LAUNCHER) val resolveInfoList: List<ResolveInfo> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { packageManager.queryIntentActivities( mainIntent, PackageManager.ResolveInfoFlags.of(0L) ) } else { packageManager.queryIntentActivities(mainIntent, 0) } return resolveInfoList.mapNotNull { resolveInfo -> try { packageManager.getApplicationInfo(resolveInfo.activityInfo.packageName, 0) } catch (e: PackageManager.NameNotFoundException) { // 应用已卸载的情况,返回null过滤掉 null } } } fun getAppsWithNotificationPermission(): List<ApplicationInfo> { val allApps = getAllApps() return allApps.filter { hasNotificationPermission(it) } } private fun hasNotificationPermission(appInfo: ApplicationInfo): Boolean { return when { // Android 13+ 用官方专属API,直接且准确 Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.areNotificationsEnabledForPackage(appInfo.packageName, android.os.Process.myUserHandle()) } // Android 8.0到12,用AppOps常量替代硬编码字符串 Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { appOpsManager.unsafeCheckOpNoThrow( AppOpsManager.OP_POST_NOTIFICATION, appInfo.uid, // 关键:使用目标应用的UID appInfo.packageName ) } else { @Suppress("DEPRECATION") appOpsManager.checkOpNoThrow( AppOpsManager.OP_POST_NOTIFICATION, appInfo.uid, appInfo.packageName ) } mode == AppOpsManager.MODE_ALLOWED } // Android 8.0以下,fallback到字符串类型的Op else -> { val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager @Suppress("DEPRECATION") val mode = appOpsManager.checkOpNoThrow( "android:post_notification", appInfo.uid, appInfo.packageName ) mode == AppOpsManager.MODE_ALLOWED } } } }
关键修改说明
- 替换UID为目标应用的UID:把
Process.myUid()改成appInfo.uid,确保我们查询的是目标应用的权限状态。 - 分版本适配API:
- Android 13+直接用
NotificationManager.areNotificationsEnabledForPackage,这是官方推荐的方式,避免AppOps的复杂逻辑。 - Android 8.0+用AppOps的常量
OP_POST_NOTIFICATION替代硬编码字符串,减少出错概率。
- Android 13+直接用
- 简化逻辑层级:把权限检查和应用信息直接绑定,避免额外的
packageName传参,减少出错可能。
额外注意事项
- 确保你的APP在Manifest中声明了
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />权限,并且在Android 11+设备上已经请求并获得了该权限,否则可能无法获取完整的应用列表。 - 可以在权限检查方法中添加异常捕获,比如处理目标应用UID无效的极端情况,但一般
ApplicationInfo中的uid都是有效的。
内容来源于stack exchange




