Android:停止前台服务导致应用程序崩溃

时间:2020-04-29 16:03:00

标签: android android-service foreground-service

先决条件: 作为对我的应用程序的要求的一部分,我需要确保该应用程序在后台运行时不会被Android系统关闭(杀死)。为此,尽管我没有在后台执行任何实际过程,但我只是在维护应用程序状态的情况下实现了Foreground服务。一切正常,除了一件事对我来说不十分清楚如何解决。

问题: 有时(暂时只有一次),我会收到此异常:

android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): 

当我试图停止前台服务而实际上没有启动它时,抛出此异常。

所以,我的问题是-有没有一种方法可以正确地停止前台服务,在实际停止前台服务之前确保它没有运行? 目前,我发现我可以为我的服务使用静态实例,并在停止服务之前将其与null进行比较,或者获取当前正在运行的所有服务的列表。但是所有这些看起来都像是一些“黑客”解决方法。

以下代码: MyForegroundService:

public class ForegroundService extends Service {

public static final int NOTIFICATION_ID = 1;
public static final String CHANNEL_ID = "SessionForegroundServiceChannel";
public static final String ACTION_FOREGROUND_START = "ACTION_FOREGROUND_START";
public static final String ACTION_FOREGROUND_STOP = "ACTION_FOREGROUND_STOP";

public static void startForegroundService(Context context) {
    Intent intent = new Intent(context, ForegroundService.class);
    intent.setAction(ForegroundService.ACTION_FOREGROUND_START);
    ContextCompat.startForegroundService(context, intent);
}

public static void stopForegroundService(Context context) {
    Intent intent = new Intent(context, ForegroundService.class);
    intent.setAction(ForegroundService.ACTION_FOREGROUND_STOP);
    ContextCompat.startForegroundService(context, intent);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (ACTION_FOREGROUND_START.equals(intent.getAction())) {
        createNotificationChannel();
        Intent stopForegroundIntent = new Intent(this, ForegroundServiceBroadcastReceiver.class);
        PendingIntent pendingLogoutIntent = PendingIntent.getBroadcast(this,
                0, stopForegroundIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                        ? null
                        : getString(R.string.app_short_name))
                .setContentText(getString(R.string.foreground_description))
                .setColor(getResources().getColor(R.color.color))
                .setSmallIcon(R.drawable.ic_notification)
                .addAction(R.drawable.ic_logout, getString(R.string.logout), pendingLogoutIntent)
                .build();
        startForeground(NOTIFICATION_ID, notification);
    } else if (ACTION_FOREGROUND_STOP.equals(intent.getAction())) {
        stopForeground(true);
        stopSelf();
    }
    return START_STICKY;
}

private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel serviceChannel = new NotificationChannel(
                CHANNEL_ID,
                getString(R.string.app_name),
                NotificationManager.IMPORTANCE_LOW
        );
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(serviceChannel);
    }
}


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

}

<service
        android:name=".ui.ForegroundService"
        android:exported="false"
        android:stopWithTask="true"/>

我还具有BroadcastReceiver和EventBus来监听一些事件并根据这些事件停止前台。

你们能帮我吗?

1 个答案:

答案 0 :(得分:1)

让我在@Pawel评论的内容中添加更多详细信息:

如果没有在调用Context.startForegroundService的三秒钟之内调用Service.startForeground,则会出现此异常。

完整的解决方案如下所示:

对于需要停止前台服务的情况,您需要执行以下操作(伪代码):

if (action == START_FOREGROUND) {
    ...
    startForeground(NOTIFICATION_ID, notification);
} else if (action == STOP_FOREGROUND) {
    startForeground(NOTIFICATION_ID, closeNotification); //in case it wasn't started before
    stopForeground(true);
    stopSelf();
}

即使它并不明显,并且任何文档都没有直接说明,当您需要停止前台时,您需要开始前台,然后再停止(如果有)还没开始)。

感谢@Pawel的提示。