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

时间:2019-03-21 09:14:59

标签: android sharedpreferences android-service android-service-binding

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

这是流程

  

当用户启用切换按钮时

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

当用户禁用切换按钮时

unbindService()    to stop getting updates    
stopForeGround()          
stopService()      stop background work  

用户退出活动时

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()没有被调用)?
我也经历过How to check if a service is running on Android?,但不幸的是,所提供的解决方案和方法已被弃用。

我有一些基于greeble31建议的解决方法。

Stll寻找更通用的方法。因此,对它进行了赏金。

4 个答案:

答案 0 :(得分:2)

您应该考虑从另一个方向解决问题。

尝试推断系统何时杀死了您的Service是有问题的。应用(Services)终止时不会收到通知,因此很难检测到这种情况。但是,找出Service开始的时间很简单(onCreate())。这是正确方法的线索。

很显然,您了解ActivityService之间交换信息的正确方法是通过绑定。这是解决方案的另一个关键部分。

解决方案

您应该延长Service的生命周期。现在看来,您创建Service的目的是为了执行长时间运行的任务。 Service是在您希望任务开始时创建的,并在任务结束时终止。换句话说,Service和“任务”的范围是相同的。

相反,您应该使“任务”成为Service的子元素; Service的存在时间应比“任务”更长。尤其是,Service启动时,Activity应该可用(如果没有其他原因要向Activity传达有关“任务”状态的信息)。

无论何时Activity开始,它都应绑定到Service,并向Service注册一个侦听器。 Service会在A.)新注册监听者或B.“任务”更改状态(启动或停止)时调用此监听器。因此,只要Activity绑定到Service,就始终可以立即获知状态变化。

Activity希望“任务”更改状态时,即使Service,它也应通过绑定(或可能是startService()调用)来通知Service已经开始)。 Service然后将启动/停止后台线程(或完成“任务”的所有操作),并通知所有侦听器。

如果Service已运行“任务”已有一段时间,并且“活动”突然弹出,则Service显然知道“任务”存在,并且{{ 1}}将在绑定后立即通过侦听器获取该信息。

Activity停止时,应通过注销其侦听器并与Activity解除绑定来进行清理。

此方法无需使用Service,也无需检测SharedPreferences何时停止。每当创建Service时,显然“任务”没有运行,因此Service将始终知道要提供给Service的正确答案。而且,由于必须先创建Activity才能绑定到Service,所以不存在操作顺序问题。

答案 1 :(得分:2)

只是一种解决方法。不是一般的解决方案。

我重新设计了。现在的流程是

  

进入活动

unbindService()
  

离开活动

startService()
  

启用切换

stopService()
  

禁用切换

SERVICE_START_COUNTER

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

为此,我在服务中使用一个int变量SERVICE_START_COUNTER,并在onStartCommand()中增加其值。当活动绑定到服务时,我会收到public boolean isServiceRunning(int SERVICE_START_COUNTER) { return SERVICE_START_COUNTER == 0 ? false : true; } 。根据计数器值,我可以区分服务是否正在运行

public void syncUI(boolean isServiceRunning)
{
   setToggleState(isServiceRunning);
   setAniamtionState(isServiceRunning);
}

并且基于此值,我编写了一种同步UI的方法。

onBind() // SERVICE_START_COUNTER = 0

在不同情况下的SERVICE_START_COUNTER值?

  

1)首次输入活动(仍然禁用切换)

onBind()           // SERVICE_START_COUNTER = 0
onStartCommand()   // SERVICE_START_COUNTER = 1
  

2)首次输入“活动”(用户启用切换)

onBind()           // SERVICE_START_COUNTER = 1
  

3)第二次重新输入活动(已启用切换)

onBind()           // SERVICE_START_COUNTER = 0
  

4)服务被操作系统杀死。重新输入活动

{{1}}

为什么它不是通用解决方案?

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

答案 2 :(得分:0)

您的问题是由您使用Bounded Foreground服务引起的。参见: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()时才会停止。即使应用程序进入后台,该服务也不会停止。

另外,您可以使用广播结果,实时数据或EventBus方法在服务和活动之间进行同步/通信。

我希望这个答案会有所帮助。抱歉,这不是您想要的。这只是一个主意。

答案 3 :(得分:0)

尝试覆盖Service的onTaskRemoved()方法,并将该方法中的切换值更新为false。看看这个Documentation