如何将切换按钮状态与前台服务同步?

8

我有一个前台服务,通过在活动中的切换按钮启动和停止。为了跟踪服务是否正在运行,我使用了一个变量,将其存储在SharedPreference中。

以下是流程:

当用户启用切换按钮时

startService()     start background work  
makeForeGround()              
bindService()      to get update

当用户禁用切换按钮时
unbindService()    to stop getting updates    
stopForeGround()          
stopService()      stop background work  

当用户离开Activity时

unbindService(); To stop getting the update. 
                 But the service is running for background work

当用户重新进入活动时
if(Service is running)
Show toggle button state as enabled 
bindService() 
我已经完成了所有这些。 唯一的问题是:
有时我的服务会被操作系统杀掉。在这种情况下,我没有办法更新我的SharedPreference变量。因此,无法将我的切换按钮与服务同步。
如何得到通知,以便知道服务已经被操作系统杀掉?(因为在这种情况下,onDestroy()不会被调用)我还研究了如何检查Android上运行的服务?,但可悲的是提供的解决方案和方法已经过时。
4个回答

3
您应该考虑从不同的方向解决您的问题。尝试推断系统何时杀死了您的“Service”是有问题的。当“Services”被终止时,应用程序不会收到通知,因此检测此条件很困难。然而,发现一个“Service”的启动时间是微不足道的(“onCreate()”)。这是正确方法的线索。
显然,您了解在“Activity”和“Service”之间交换信息的正确方法是通过绑定。这是解决方案的另一个关键部分。
解决方案是:扩展“Service”的生命周期。现在,看起来您创建了“Service”来执行长时间运行的任务。当您想要开始任务时,创建“Service”,并在任务结束后立即终止它。换句话说,“Service”的范围和“task”的范围是相同的。
相反,您应该将“task”作为“Service”的子项;“Service”的存在时间应该比“task”稍微长一些。特别是,“Service”应该在每次启动“Activity”时都可用(如果没有其他原因,则与“Activity”通信以获取“task”的状态信息)。
每当“Activity”启动时,都应该绑定到“Service”,并在“Service”中注册一个监听器。只要绑定到“Service”,“Service”就会在以下情况下调用此监听器:A)监听器新注册,或B)“task”更改状态(启动或停止)。因此,只要绑定到“Service”,“Activity”就会立即得到有关状态更改的信息。
当“Activity”希望“task”更改状态时,它应通过绑定(或可能是“startService()”调用,即使“Service”已经运行)通知“Service”。然后,“Service”将启动/停止后台线程(或者您为完成“task”所做的任何其他操作),并通知任何监听器。
如果“Service”已经运行了一段时间的“task”,而突然弹出了“Activity”,那么“Service”显然知道“task”存在,并且“Activity”将在绑定时通过监听器获取该信息。
当“Activity”停止时,应通过注销其监听器和解除与“Service”的绑定来进行清理。
这种方法不需要使用SharedPreferences或检测Service何时停止。每当创建Service时,显然"任务"没有在运行,因此Service总是知道要向Activity提供的正确答案。由于Service必须在绑定到Activity之前被创建,所以不存在操作顺序问题。

很好的解释。即使我也在考虑类似的解决方案。但是当涉及到同步我的切换按钮,如果服务被操作系统杀死,我仍然会陷入困境。 - Rohit Singh
这应该不是问题,因为:如果操作系统杀死了“Service”,整个应用程序也会被杀死。这意味着所有的“Activities”都将被杀死。当一个新的“Activity”启动时,它将绑定。如果“Service”已经绑定,那么它必须在运行中。如果“Service”正在运行,则必须知道任务的状态。 - greeble31

2

只是一种权宜之计,不是通用解决方案。

我重新设计了。现在流程如下:

进入该活动

bindService()

离开Activity
unbindService()

Enable Toggle

startService()

Disable Toggle

stopService()

如何跟踪服务是否正在运行?

为此,我在服务中使用一个 int 变量 SERVICE_START_COUNTER,并在 onStartCommand() 中增加其值。当 Activity 绑定到服务时,我会接收 SERVICE_START_COUNTER。根据计数器的值,我可以区分服务是否正在运行。

public boolean isServiceRunning(int SERVICE_START_COUNTER)
{
    return SERVICE_START_COUNTER == 0 ? false : true;
}

根据这个值,我编写了一个同步UI的方法。
public void syncUI(boolean isServiceRunning)
{
   setToggleState(isServiceRunning);
   setAniamtionState(isServiceRunning);
}

不同情况下的SERVICE_START_COUNTER值是多少?

1)第一次进入Activity(切换仍然被禁用)

onBind() // SERVICE_START_COUNTER = 0

2) 第一次进入活动(用户启用切换)
onBind()           // SERVICE_START_COUNTER = 0
onStartCommand()   // SERVICE_START_COUNTER = 1

3) 第二次重新进入活动(切换已启用)
onBind()           // SERVICE_START_COUNTER = 1

4)服务被操作系统杀死。重新进入活动。
onBind()           // SERVICE_START_COUNTER = 0

为什么这不是一个通用解决方案?

由于绑定需要时间(因为onServiceConnected()是异步调用的),因此结果可能不会立即可用。因此,我首先在Splash Activity中检查服务的状态,并在Utility类中更新类似的计数器。


0
尝试重写您的Service的onTaskRemoved()方法,并在该方法中将切换值更新为false。请查看此文档

0

你的问题是由于使用了有界前台服务所导致的。请参考:https://developer.android.com/guide/components/bound-services

有界服务通常只在为其他应用程序组件提供服务时才存在,并且不会无限期地在后台运行。

基本上,你的服务存在取决于是否已绑定。

另一种选择是不使用Bind方法,而是使用Context.startService()或Context.startForegroundService()来启动服务并让它永久运行。就像这样:

 Intent intent = new Intent(getApplicationContext(), ForegroundService.class);
 intent.setAction(ForegroundService.ACTION_START_FOREGROUND_SERVICE);
 startService(intent);

ForegroundService类看起来像这样:

public class ForegroundService extends Service {

private static final String TAG_FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE";
public static final String ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE";

public ForegroundService () {
}

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public void onCreate() {
    super.onCreate();
    Log.d(TAG_FOREGROUND_SERVICE, "My foreground service onCreate().");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if(intent != null)
    {
        String action = intent.getAction();

        switch (action)
        {
            case ACTION_START_FOREGROUND_SERVICE:
                startForegroundService();
                break;
            case ACTION_STOP_FOREGROUND_SERVICE:
                stopForegroundService();
                break;
        }
    }
    return super.onStartCommand(intent, flags, startId);
}

private void startForegroundService()
{
    // Build the notification.
    // ... lot of code ...
    Notification notification = builder.build();

    // Start foreground service.
    startForeground(1, notification);
}

private void stopForegroundService()
{
    // Stop foreground service and remove the notification.
    stopForeground(true);

    // Stop the foreground service.
    stopSelf();
}
}

在这种情况下,您不需要同步服务状态,因为它将尽可能地保持活动状态,并且只有在调用stopSelf()时才会停止。即使应用程序进入后台,服务也不会停止。
另外,您可以使用Broadcast result、livedata或EventBus方法在服务和活动之间进行同步/通信。
希望这个回答能够帮到您。如果这不是您想要的,请谅解,这只是一个想法。

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