广播接收器在 Android 8 的前台服务中不起作用

Posted

技术标签:

【中文标题】广播接收器在 Android 8 的前台服务中不起作用【英文标题】:Broadcast receiver doesn't work inside foreground service in Android 8 【发布时间】:2019-01-07 14:03:18 【问题描述】:

我正在尝试创建一个应用程序,允许用户在设定的时间间隔内或手动使设备静音。我还希望用户能够输入更复杂的规则,例如仅在连接到某个 WiFi 网络、蓝牙设备或进入地理围栏时才使手机静音。但是,在静音模式下,我希望允许用户创建电话仍应响铃的联系人列表。

为此,我创建了一个前台服务,我在其中注册了一个广播接收器。但是,在活动离开前台仅几分钟后,接收器就会停止按时工作并不时更新。

此问题出现在装有 android 8 的设备(华为/荣耀 7x)中。

注意:我尝试将应用添加到打盹白名单并禁用供应商特定的电池优化,但没有任何改变。

下面是我的代码的 MCVE。每当触发服务中的接收器时,一些 int 值就会增加,然后它们会显示在通知中。在我的设备上,自从活动离开前台几分钟后,值停止增加:

清单权限

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>

清单声明服务

<service android:name=".PhoneService"/>

在 Activity 内部启动 Service

//for the sake of simplicity, I omit the code where runtime permissions are requested
Intent foregroundIntent = new Intent(this, PhoneService.class);
foregroundIntent.putExtra(PhoneService.EXTRA_ACTION, PhoneService.ACTION_START);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
    startForegroundService(foregroundIntent);
 else 
    startService(foregroundIntent);

服务类

public class PhoneService extends Service 

private static final String NOTIFICATION_CATEGORY = "notification_category";
private static final int NOTIFICATION_REQUEST_CODE = 0;
private static final String CHANNEL_ID = "test_channel";
private static final String CHANNEL_NAME = "test channel";
private static final int NOTIFICATION_ID = 1;
public static final String EXTRA_ACTION = "extra_action";
public static final String ACTION_START = "action_start";
public static final String ACTION_STOP = "action_stop";
private static final int STOP_ID = 2;
private int phoneReceiveCount = 0;
private int btReceiveCount = 0;
BroadcastReceiver phoneReceiver;

@Nullable
@Override
public IBinder onBind(Intent intent) 
    return null;


@Override
public void onCreate() 
    super.onCreate();
    final IntentFilter intentFilter = new IntentFilter();
    //adding some filters
    intentFilter.addAction("android.intent.action.PHONE_STATE");
    intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
    intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    this.phoneReceiver = new BroadcastReceiver() 
        @Override
        public void onReceive(Context context, Intent intent) 
            //update the count and show it in the notification body
            //used only to see if the receiver works
            String action = intent.getAction();
            if (action != null && action.equals("android.intent.action.PHONE_STATE")) 
                phoneReceiveCount++;
             else if (action != null && (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) || action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) 
                btReceiveCount++;
            
            createNotification(context);
        
    ;
    registerReceiver(phoneReceiver, intentFilter);


@Override
public int onStartCommand(Intent intent, int flags, int startId) 
    String action = intent.getStringExtra(EXTRA_ACTION);
    if (action.equals(ACTION_START)) 
        createNotification(this);
     else if (action.equals(ACTION_STOP)) 
        stopSelf();
    
    //also tried with START_STICKY
    return START_REDELIVER_INTENT;


@Override
public void onDestroy() 
    super.onDestroy();
    unregisterReceiver(phoneReceiver);


private void createNotification(Context context) 
    //intent to open app
    Intent entryActivityIntent = new Intent(context, MainActivity.class);
    entryActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    PendingIntent pendingEntryActivityIntent = PendingIntent.getActivity(context, NOTIFICATION_REQUEST_CODE, entryActivityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    //intent to stop foreground service
    Intent stopServiceIntent = new Intent(context, PhoneService.class);
    stopServiceIntent.putExtra(PhoneService.EXTRA_ACTION, PhoneService.ACTION_STOP);
    stopServiceIntent.addCategory(NOTIFICATION_CATEGORY);
    PendingIntent pendingStopForeground = PendingIntent.getService(context, STOP_ID, stopServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    //build notification
    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
        NotificationChannel channel = new NotificationChannel(
                CHANNEL_ID,
                CHANNEL_NAME,
                NotificationManager.IMPORTANCE_LOW);
        channel.enableVibration(false);
        channel.enableLights(false);
        notificationManager.createNotificationChannel(channel);
    
    String contentText = "phoneReceiveCount: " + String.valueOf(phoneReceiveCount) + "\nbtReceiveCount: " + String.valueOf(btReceiveCount);
    NotificationCompat.Builder notificationBuilder =
            new NotificationCompat.Builder(context, CHANNEL_ID)
                    .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
                    .setSmallIcon(R.drawable.ic_service_on)
                    .setContentTitle("This is the title")
                    .setContentText(contentText)
                    .addAction(R.drawable.ic_stop, "Stop", pendingStopForeground)
                    .setStyle(new NotificationCompat.BigTextStyle().bigText(contentText))
                    .setContentIntent(pendingEntryActivityIntent);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) 
        notificationBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
    
    startForeground(NOTIFICATION_ID, notificationBuilder.build());

【问题讨论】:

它是否可以在模拟器或任何带有股票 android 的设备上运行? 感谢您的有趣@bnayagrawal。在带有 android 8 的模拟器中,它可以完美运行。它也适用于通过 ADB 连接的 Android 8 物理设备。但是如果我从电脑上拔下设备,在应用不再处于前台后,接收器会按时停止工作。 你解决了吗?我有同样的问题.. @EmanueleMazzante 你有什么解决办法吗?我在使用 Huwai Honor 8C 设备时遇到了同样的问题。 @QadirHussain 没有解决方案。我认为这与华为/荣耀 Android 8 版本有关 【参考方案1】:

从 Android 8 开始,后台服务和广播(如广播接收器)有一定的限制,必须在运行时使用 context.registerReceiver 注册。

详细解释请看这个链接:

https://developer.android.com/about/versions/oreo/background#broadcasts

您的目标 SDK 高于 25。降低可能会解决问题,但不建议这样做。请按照文档中的指南和步骤解决问题。

【讨论】:

谢谢,我知道 Android 8 中广播和后台服务的限制。我认为我的接收器在运行时的 onCreate 方法中已正确注册。不是吗? 我不明白为什么,有时我在前台服务中的广播接收器工作,有时却没有 这是否意味着我们不能在服务中注册广播接收器?

以上是关于广播接收器在 Android 8 的前台服务中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

通知在android中不起作用

Android - 前台在 Oreo 中不起作用。操作系统在一段时间后终止服务

Android O 在打盹模式下前台服务未接收位置更新

android 如何实现后台时用通知栏显示有新的消息,当在前台时不显示通知

Android中的广播接收器不起作用

如何在 Android API 30 设备中使用静态广播接收器或类似服务?