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

Manifest注册的Broadcast Receiver在Android Pie上失效问题

解决Android Pie上蓝牙广播接收器失效的问题

嘿,我来帮你搞定这个头疼的问题!你说添加了前台常驻通知后,Marshmallow设备正常,但Pie设备哪怕App开着也收不到蓝牙状态变化的广播,这大概率是Android 8.0+对广播和后台服务的新限制搞的鬼,结合你的代码,我给你几个针对性的解决方案:

1. 把静态注册的广播改成动态注册

Android 8.0(API 26)开始,系统对静态注册的隐式广播管得特别严,虽然官方说BluetoothAdapter.ACTION_STATE_CHANGED属于例外,但不少厂商的定制ROM可能有额外限制,而且静态注册的接收器在App进程状态波动时容易被系统忽略。

建议你把广播接收器移到前台服务里动态注册,只要服务一直在前台跑,接收器就能稳稳监听蓝牙状态:

修改OngoingService.kt:

import android.app.*
import android.content.Intent
import android.content.IntentFilter
import android.os.IBinder
import android.support.v4.app.NotificationCompat
import android.content.Context
import android.os.Build
import android.support.annotation.RequiresApi
import android.support.v4.app.NotificationCompat.PRIORITY_MIN

class OngoingService : Service() {
    private lateinit var bluetoothReceiver: BReceivers

    override fun onCreate() {
        super.onCreate()
        // 动态注册蓝牙广播接收器
        bluetoothReceiver = BReceivers()
        val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
        registerReceiver(bluetoothReceiver, filter)
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        startForeground()
        return START_STICKY // 确保服务被意外杀死后能自动重启
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun startForeground() {
        val channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            initChannels("fearApp_service", "Fear Appeals Service")
        } else {
            ""
        }
        val notificationIntent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(
            this, 0, notificationIntent, 
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
        )
        val notificationBuilder = NotificationCompat.Builder(this, channelId )
        val notification = notificationBuilder
            .setOngoing(true)
            .setContentTitle(getString(R.string.app_name))
            .setContentText("Fear Appeals is running background")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setPriority(PRIORITY_MIN)
            .setCategory(Notification.CATEGORY_SERVICE)
            .setContentIntent(pendingIntent)
            .build()
        startForeground(101, notification)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun initChannels(channelId: String, channelName: String): String {
        val channel = NotificationChannel(
            channelId, channelName, NotificationManager.IMPORTANCE_LOW // 改成LOW,和通知优先级匹配
        )
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        channel.description = "Ongoing notification to keep the app opened."
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
        return channelId
    }

    override fun onDestroy() {
        super.onDestroy()
        // 服务销毁时记得注销接收器,避免内存泄漏
        unregisterReceiver(bluetoothReceiver)
    }
}

改完后,记得把AndroidManifest.xml里静态注册的<receiver>标签删掉,别重复注册啦。

2. 修正前台服务的启动方式

在Android 8.0+,启动前台服务必须用startForegroundService(),不能再用startService()了,不然系统会直接抛出异常,甚至把服务杀掉。快改一下OngoingApp.kt:

import android.app.Application
import android.content.Intent
import android.os.Build

class OngoingApp : Application() {
    override fun onCreate() {
        super.onCreate()
        val serviceIntent = Intent(this, OngoingService::class.java)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(serviceIntent)
        } else {
            startService(serviceIntent)
        }
    }
}

3. 补全必要的权限(可选但建议)

虽然监听蓝牙状态本身不需要位置权限,但有些Android 9设备的系统限制比较严,要求App有位置权限才能接收蓝牙相关广播。你可以在AndroidManifest.xml里加上位置权限,然后在运行时申请:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

再在MainActivity里加个权限申请的逻辑:

class MainActivity : AppCompatActivity() {
    private val PERMISSION_REQUEST_LOCATION = 100

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 运行时申请位置权限(Android 6+需要)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) != android.content.pm.PackageManager.PERMISSION_GRANTED) {
                requestPermissions(arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION), PERMISSION_REQUEST_LOCATION)
            }
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == PERMISSION_REQUEST_LOCATION) {
            if (grantResults.isNotEmpty() && grantResults[0] == android.content.pm.PackageManager.PERMISSION_GRANTED) {
                // 权限拿到了,正常使用就行
            } else {
                // 用户拒绝了权限,可能影响蓝牙广播接收,建议提示用户开启
            }
        }
    }
}

4. 优化通知渠道的配置

你之前的通知渠道用了IMPORTANCE_DEFAULT,但通知优先级是PRIORITY_MIN,两者不匹配,可能导致通知显示不正常,进而影响前台服务的稳定性。我已经在上面的OngoingService代码里把渠道重要性改成了IMPORTANCE_LOW,和PRIORITY_MIN对应上了,这样就没问题啦。

最后总结

按照上面的步骤改完,应该就能解决Pie设备上蓝牙广播接收器失效的问题了。核心原因就是Android 8.0+对静态广播和后台服务的限制,改用动态注册广播接收器+正确启动前台服务是最靠谱的解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:14:03