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

Android电话录音服务持续崩溃求助:附来电检测BroadcastReceiver代码

嘿,我看到你在做来电录音功能时,遇到了BroadcastReceiver调用AudioService就崩溃的问题,结合你给出的代码片段,我来帮你排查几个最可能的坑和对应的解决办法:

1. 权限没配全或没动态申请

电话录音需要好几个关键权限,少一个都可能直接崩:

  • 首先要在AndroidManifest.xml里声明静态权限:
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
    <!-- Android 13+ 还要加通知权限 -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    
  • 然后,RECORD_AUDIO和READ_PHONE_STATE是危险权限,必须在代码里动态申请,不能只靠Manifest声明。如果没申请就直接启动录音服务,肯定会崩溃。

2. Android 8.0+ 后台服务启动限制

从Android 8.0开始,直接启动后台服务会被系统禁止,必须用前台服务,还要显示一个通知告诉用户“当前有服务在运行”:

  • 启动服务时,要判断系统版本:Android 8.0及以上用startForegroundService(),之前的版本用startService()
  • 在AudioService的onCreate()方法里,必须调用startForeground()创建通知,否则系统会直接杀掉你的服务,导致崩溃。

3. BroadcastReceiver里的上下文问题

如果你的Receiver是静态注册的,直接用context.startService()可能会因为上下文生命周期问题导致崩溃,建议换成context.applicationContext来启动服务,避免内存泄漏或者上下文无效的情况。

4. 空指针隐患

你代码里的state可能为空,比如某些设备的TelephonyManager不会正确传递状态值,直接用when(state)会触发空指针异常。最好先加个空判断:val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE) ?: return,如果state为空就直接返回,不执行后续逻辑。


修复后的代码示例

修改后的PhoneStateReceiver:

class PhoneStateReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // 先判断状态是否为空,避免空指针
        val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE) ?: return
        val incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)

        when(state) {
            TelephonyManager.EXTRA_STATE_RINGING -> {
                // 先检查权限是否已授予
                val hasRecordPerm = ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
                val hasPhonePerm = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
                
                if (hasRecordPerm && hasPhonePerm) {
                    // 使用applicationContext启动服务
                    val serviceIntent = Intent(context.applicationContext, AudioService::class.java).apply {
                        putExtra("INCOMING_NUMBER", incomingNumber)
                    }
                    // 根据系统版本选择启动方式
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        context.startForegroundService(serviceIntent)
                    } else {
                        context.startService(serviceIntent)
                    }
                } else {
                    Toast.makeText(context, "需要录音和电话状态权限才能开启录音", Toast.LENGTH_SHORT).show()
                }
            }
            // 来电挂断时停止服务
            TelephonyManager.EXTRA_STATE_IDLE -> {
                val stopIntent = Intent(context.applicationContext, AudioService::class.java)
                context.stopService(stopIntent)
            }
        }
    }
}

AudioService的核心代码:

class AudioService : Service() {
    private val NOTIFICATION_ID = 101
    private val CHANNEL_ID = "CALL_RECORD_CHANNEL"

    override fun onCreate() {
        super.onCreate()
        // 创建通知渠道(Android 8.0+ 必需)
        createNotificationChannel()
        // 启动前台服务,必须显示通知
        startForeground(NOTIFICATION_ID, createRecordingNotification())
        // 这里初始化你的录音逻辑
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val incomingNumber = intent?.getStringExtra("INCOMING_NUMBER")
        // 开始录音的逻辑...
        return START_STICKY
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(CHANNEL_ID, "来电录音服务", NotificationManager.IMPORTANCE_LOW).apply {
                description = "正在录制来电通话"
            }
            val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    private fun createRecordingNotification(): Notification {
        val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder(this, CHANNEL_ID)
        } else {
            @Suppress("DEPRECATION")
            Notification.Builder(this)
        }
        return builder.setContentTitle("来电录音中")
            .setContentText("正在录制与${intent?.getStringExtra("INCOMING_NUMBER") ?: "未知号码"}的通话")
            .setSmallIcon(R.drawable.ic_recording) // 替换成你的图标
            .build()
    }

    override fun onBind(intent: Intent): IBinder? = null

    override fun onDestroy() {
        super.onDestroy()
        // 停止录音,释放资源
    }
}

Manifest里的Receiver和Service注册:

<receiver android:name=".PhoneStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE" />
    </intent-filter>
</receiver>

<service android:name=".AudioService" android:foregroundServiceType="microphone" />

内容的提问来源于stack exchange,提问作者Hasan A Yousef

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:12:10