You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android应用监听紧急号码外呼:广播接收失败与权限问题排查

问题分析与解决方案

核心原因

你的问题源于Android版本迭代带来的权限与广播机制限制,分不同API级别具体说明:

1. API 23~28(Android 6.0~9.0)

PROCESS_OUTGOING_CALLS属于危险权限,仅在Manifest中声明无法生效,必须在运行时动态申请权限。你当前代码未处理动态权限请求,导致接收器无法触发。

2. API 29+(Android 10及以上)

PROCESS_OUTGOING_CALLS权限已被官方废弃,NEW_OUTGOING_CALL广播的接收权限被严格限制:

  • 仅默认电话应用可直接接收该广播
  • 非默认应用无法通过静态广播接收器监听,只能通过监听通话记录内容变化获取外呼事件

针对性解决方案

针对API 23~28版本

  1. 动态申请PHONE权限组
    在Activity或Fragment中添加权限申请逻辑:
private val REQUEST_PHONE_PERMISSIONS = 1001

fun requestPhonePermissions() {
    val permissions = arrayOf(
        Manifest.permission.PROCESS_OUTGOING_CALLS,
        Manifest.permission.READ_PHONE_STATE
    )
    if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, permissions, REQUEST_PHONE_PERMISSIONS)
    }
}

// 处理权限申请结果
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == REQUEST_PHONE_PERMISSIONS) {
        if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
            // 权限申请成功,广播接收器可正常工作
        } else {
            // 权限被拒绝,功能无法使用
        }
    }
}
  1. 在应用启动时调用requestPhonePermissions()方法,获取权限后再测试。

针对API 29+版本

改用内容观察者监听通话记录实现需求:

class CallLogObserver(private val context: Context, private val handler: Handler) : ContentObserver(handler) {
    override fun onChange(selfChange: Boolean, uri: Uri?) {
        super.onChange(selfChange, uri)
        // 查询最新通话记录,判断是否为外呼电话
        val projection = arrayOf(
            CallLog.Calls.TYPE,
            CallLog.Calls.NUMBER
        )
        val cursor = context.contentResolver.query(
            CallLog.Calls.CONTENT_URI,
            projection,
            null,
            null,
            CallLog.Calls.DATE + " DESC LIMIT 1"
        )
        cursor?.use {
            if (it.moveToFirst()) {
                val callType = it.getInt(it.getColumnIndexOrThrow(CallLog.Calls.TYPE))
                val number = it.getString(it.getColumnIndexOrThrow(CallLog.Calls.NUMBER))
                if (callType == CallLog.Calls.OUTGOING_TYPE) {
                    // 检测到外呼电话,执行自定义逻辑(如判断是否为紧急号码)
                    Toast.makeText(context, "外呼号码:$number", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}

// 在Activity中注册/注销观察者
private lateinit var callLogObserver: CallLogObserver

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    callLogObserver = CallLogObserver(this, Handler(Looper.getMainLooper()))
    // 申请READ_CALL_LOG权限(API29+必需)
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CALL_LOG), 1002)
    } else {
        contentResolver.registerContentObserver(
            CallLog.Calls.CONTENT_URI,
            true,
            callLogObserver
        )
    }
}

override fun onDestroy() {
    super.onDestroy()
    contentResolver.unregisterContentObserver(callLogObserver)
}

关于Google Play限制补充

  • Google Play对电话类权限审核严格,即便签署声明,非必要使用相关权限的应用仍可能被拒上架
  • 若仅需检测紧急号码,可结合TelecomManager.isEmergencyNumber()方法辅助判断,但前提是能获取到外呼号码

额外说明

  • MANAGE_OWN_CALLS权限仅适用于应用自身发起的通话,无法监听系统内其他应用的外呼事件,对你的场景无效
  • API28模拟器测试无响应,大概率是未动态申请权限,解决权限问题后即可正常触发广播

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 21:12:42