简体   繁体   English

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

[英]Android: Stop Foreground Service causing Application crash

Prerequisites: As a part of the requirement for my application, I need to make sure that the application won't be closed (killed) by the Android system while in background.先决条件:作为我的应用程序要求的一部分,我需要确保应用程序在后台不会被 Android 系统关闭(杀死)。 For this purpose I implemented Foreground service, even though I don't do any actual process in background, just maintaining the state of the application.为此,我实现了前台服务,即使我没有在后台执行任何实际进程,只是维护应用程序的 state。 Everything works just fine, except one thing which is not fully clear to me how to fix.一切都很好,除了我不完全清楚如何解决的一件事。

The issue: Sometimes (only once, for now), I receive this exception:问题:有时(目前只有一次),我收到此异常:

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

This exception is thrown when I'm trying to stop the foreground service while it wasn't actually started.当我试图停止前台服务但实际上并未启动时,会引发此异常。

So, my question is - is there is a way to stop foreground service properly, making sure that it is not running before actually stopping it?所以,我的问题是 - 有没有办法正确停止前台服务,确保它在实际停止之前没有运行? What I found at the moment is that I can have static instance for my service and compare to null before stopping service, or get the list of all services currently running.我目前发现的是,我可以为我的服务提供 static 实例,并在停止服务之前与 null 进行比较,或者获取当前正在运行的所有服务的列表。 But all these look like some "hack" workarounds.但所有这些看起来都像是一些“黑客”解决方法。

Here some code: MyForegroundService:这里有一些代码: 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"/>

I also have BroadcastReceiver and EventBus to listen to some events and stop foreground depending on those events.我还有 BroadcastReceiver 和 EventBus 来监听一些事件并根据这些事件停止前台。

Can you please help me, guys?你们能帮帮我吗,伙计们?

Let me add more details to what @Pawel commented:让我为@Pawel 评论的内容添加更多细节:

You get this exception if you don't call Service.startForeground within 3 seconds of calling Context.startForegroundService that's all there's to it.如果您在调用 Context.startForegroundService 后 3 秒内未调用 Service.startForeground,则会出现此异常。

Here is how the complete solution will look like:完整的解决方案如下所示:

When it comes to the case when you need to stop a foreground service you need to do the following (pseudo code):当您需要停止前台服务时,您需要执行以下操作(伪代码):

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

Even though it is not obvious, and any documentation don't directly say that when you need to stop foreground you need to start foreground before stopping it (if it wasn't started).即使这并不明显,并且任何文档都没有直接说明当您需要停止前台时,您需要在停止前启动前台(如果它没有启动)。

Thanks @Pawel for the hint.感谢@Pawel 的提示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM