在Android 8中,前台服务中的广播接收器无法工作

5
我正在尝试创建一个应用程序,允许用户在设置的时间间隔或手动静音设备。我还希望用户能够输入更复杂的规则,例如仅在连接到特定WiFi网络、蓝牙设备或进入地理围栏时将手机静音。在静音模式下,我希望允许用户创建联系人列表,使电话仍会响铃。
为此,我创建了一个前台服务,在其中注册广播接收器。然而,只有几分钟后,自从活动离开前台后,接收器就停止按时工作并且更新也变得不规律。
这个问题发生在一个安装有Android 8系统的设备上(Huawei/Honor 7x)。
注:我尝试将应用程序添加到Doze白名单并禁用特定厂商的电池优化,但没有改变任何事情。
以下是我的代码的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"/>

启动服务的内部活动

//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());
}

}


它能在模拟器上运行还是任何带有原生安卓系统的设备上运行? - bnayagrawal
感谢您的关注@bnayagrawal。在安卓8模拟器中,它可以完美运行。同时,在通过ADB连接的安卓8物理设备上也可以运行。但是,如果我将设备从电脑上拔下来,接收器会在应用程序不再处于前台时停止工作。 - Emanuele Mazzante
@EmanueleMazzante,你有找到解决方案吗?我在华为荣耀8C设备上遇到了同样的问题。 - Qadir Hussain
@QadirHussain 暂无解决方案。我认为这与华为/荣耀Android 8发布有关。 - Emanuele Mazzante
@EmanueleMazzante 没问题 - Sanjay
显示剩余2条评论
1个回答

0

从Android 8开始,后台服务和广播等的某些限制要求使用context.registerReceiver在运行时进行注册。

请参见此链接以获取详细说明:

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

你的目标SDK版本应该在25及以上。降低SDK版本可能会解决问题,但不建议这样做。请遵循文档中的指南和步骤来解决问题。


谢谢,我知道在Android 8中广播和后台服务的限制。我认为我的接收器在onCreate方法中正确地注册了运行时。不是吗? - Emanuele Mazzante
1
我不明白为什么有时候我的前台服务内的广播接收器能够工作,而有时候却不能。 - jashgopani
这是否意味着我们不能在Service中注册BroadcastReceiver? - IgorGanapolsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接