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
相关产品推荐
相关产品推荐

