You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android O(API 27)中监听通话状态的BroadcastReceiver工作异常

解决Android O+中无法接收CALL_STATE_IDLE状态的问题

这个问题我之前也踩过坑,核心原因就是Android 8.0(API 26)推出的后台执行限制,直接限制了静态注册广播接收器的能力。

问题根源

在Android N及更早版本,你可以通过AndroidManifest.xml静态注册广播接收器来监听ACTION_PHONE_STATE_CHANGED,并完整接收所有通话状态。但从Android O开始,系统对静态注册的广播接收器做了严格管控:除了少数豁免的系统广播外,大部分隐式广播无法再通过静态注册接收。

虽然ACTION_PHONE_STATE_CHANGED属于豁免列表,但当通话结束进入CALL_STATE_IDLE状态时,你的APP大概率已经退到后台,这时候静态注册的接收器就会被系统限制,无法接收到这个状态的广播——而呼出、呼入、CALL_STATE_OFFHOOK状态触发时,APP可能还处于前台,所以能正常接收。

解决方案:动态注册广播接收器

解决这个问题的关键是动态注册广播接收器,在APP处于活跃状态时注册,退出时注销,这样就能绕过静态注册的限制,完整接收所有通话状态。

步骤1:移除静态注册(如果有)

先删掉AndroidManifest.xml中对应的广播接收器声明,比如:

<!-- 删掉这段静态注册的代码 -->
<receiver android:name=".PhoneStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE" />
    </intent-filter>
</receiver>

步骤2:动态注册接收器并处理权限

在你的Activity或Service中,动态注册接收器,同时别忘了处理READ_PHONE_STATE的动态权限(Android 6.0+必须)。下面是完整的Kotlin示例代码:

private lateinit var phoneStateReceiver: BroadcastReceiver
private val PERMISSION_REQUEST_READ_PHONE_STATE = 1001

private fun startListening(context: Context) {
    // 先检查是否拥有读取电话状态的权限
    if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE)
        != PackageManager.PERMISSION_GRANTED) {
        // 动态申请权限
        ActivityCompat.requestPermissions(context as Activity,
            arrayOf(Manifest.permission.READ_PHONE_STATE),
            PERMISSION_REQUEST_READ_PHONE_STATE)
        return
    }

    // 初始化广播接收器
    phoneStateReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val state = intent?.getStringExtra(TelephonyManager.EXTRA_STATE)
            when (state) {
                TelephonyManager.EXTRA_STATE_IDLE -> {
                    // 终于能接收到通话结束的状态了!
                    println("通话结束:CALL_STATE_IDLE")
                }
                TelephonyManager.EXTRA_STATE_OFFHOOK -> {
                    println("通话接通:CALL_STATE_OFFHOOK")
                }
                TelephonyManager.EXTRA_STATE_RINGING -> {
                    println("有电话呼入:CALL_STATE_RINGING")
                }
            }
        }
    }

    // 注册接收器,监听电话状态变化
    val filter = IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED)
    context.registerReceiver(phoneStateReceiver, filter)
}

// 在Activity销毁时注销接收器,避免内存泄漏
override fun onDestroy() {
    super.onDestroy()
    try {
        unregisterReceiver(phoneStateReceiver)
    } catch (e: IllegalArgumentException) {
        // 防止重复注销抛出异常
        e.printStackTrace()
    }
}

// 处理权限申请结果
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == PERMISSION_REQUEST_READ_PHONE_STATE) {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限申请成功,开始监听
            startListening(this)
        } else {
            // 权限被拒绝,无法监听电话状态
            println("需要授予读取电话状态权限才能正常工作")
        }
    }
}

额外注意点

如果你的监听逻辑需要在后台持续运行,比如APP退到后台后仍要监听通话状态,那你需要把接收器注册在前台Service中——因为Android O+对后台Service有严格的限制,前台Service可以保持活跃状态,确保接收器能正常接收广播。

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

火山引擎 最新活动