使用 START_STICKY 或 START_NOT_STICKY 保持后台服务不起作用

Posted

技术标签:

【中文标题】使用 START_STICKY 或 START_NOT_STICKY 保持后台服务不起作用【英文标题】:Keep Background Service using START_STICKY or START_NOT_STICKY doesn't work 【发布时间】:2018-06-26 04:25:12 【问题描述】:

我创建了一个使用服务在后台运行CountDownTimer 的应用程序。

但是当应用关闭/终止时,后台服务有时会失败或自行停止。

我曾尝试使用START_STICKYSTART_NOT_STICK,但仍然无法正常工作,无法保持服务CountDownTimer 处于活动状态。 (this the different of them)

我也尝试了我在 *** 上找到的所有建议,但同样不起作用。

这是我的代码:

SessionTimerService.java

public class SessionTimerService extends Service

    private CountDownTimer countDownTimer;
    public static boolean isServiceRunning = false;

    @Override
    public void onCreate() 
        super.onCreate();
        startTimer();
    

    @Override
    public void onDestroy() 
        isServiceRunning = false;
        super.onDestroy();
    


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        if(intent!=null && intent.getAction().equals(Constant.ACTION_START_SERVICE))
            startTimer();
        else
            stopTimer();
        
        return START_STICKY;
    

    private void stopTimer()
        stopForeground(true);
        stopSelf();
        isServiceRunning = false;
    

    private void startTimer()
        if(isServiceRunning)return;
        isServiceRunning = true;

        String formatCountDown = "%02d:%02d:%02d";
        countDownTimer = new CountDownTimer(TimeUnit.MINUTES.toMillis(1), 1000)

            @Override
            public void onTick(long millisUntilFinished) 
                String time = ""+String.format(formatCountDown,
                        TimeUnit.MILLISECONDS.toHours(millisUntilFinished),
                        TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) - TimeUnit.HOURS.toMinutes(
                                TimeUnit.MILLISECONDS.toHours(millisUntilFinished)),
                        TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(
                                TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished)));
                Timber.d("The Time : " + time);
                Intent broadcast = new Intent(Constant.EVENT_SESSION_TIMER);
                broadcast.putExtra(Constant.BROADCAST_TIMER, time);
                LocalBroadcastManager.getInstance(App.getInstance().getApplicationContext()).sendBroadcast(broadcast);

            

            @Override
            public void onFinish() 
                Toast.makeText(SessionTimerService.this, "Timer ended!", Toast.LENGTH_SHORT).show();
            
        ;
        countDownTimer.start();
    


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

App.java

我从应用程序启动CountDownTimer。这里是代码:

public class App extends Application 

    private ApplicationComponent applicationComponent;
    private static boolean isChatActivityOpen = false;
    private static boolean isSendChat = false;
    private static App instanceApp;
    private CountDownTimer countDownTimer;
    private Handler handler = new Handler();

    @Override
    public void onCreate() 
        super.onCreate();
        Fabric.with(this, new Crashlytics());

        Timber.plant(new Timber.DebugTree());

        instanceApp = this;

        // TODO: Add this project to your fabric.io
        /*Crashlytics crashlyticsKit = new Crashlytics.Builder()
                .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
                .build();
        Fabric.with(this, crashlyticsKit);*/

        if (applicationComponent == null) 
            applicationComponent = DaggerApplicationComponent.builder()
                    .applicationModule(new ApplicationModule(App.get(this)))
                    .build();
        
    

    public static App get(Context context)
        return (App) context.getApplicationContext();
    

    public ApplicationComponent getComponent()
        return applicationComponent;
    

    public static boolean isChatActivityOpen() 
        return isChatActivityOpen;
    

    public static boolean isSendChat()
        return isSendChat;
    

    public static synchronized App getInstance()
        return instanceApp;
    

    public void setConnectivityListener(ConnectivityReceiver.ConnectivityReceiverListener listener)
        ConnectivityReceiver.connectivityReceiverListener = listener;
    

    public static void setChatActivityOpen(boolean isChatActivityOpen) 
        App.isChatActivityOpen = isChatActivityOpen;
    

    public static void setSendChatStatus(boolean isSendChat)
        App.isSendChat = isSendChat;
    

    public void startTimer()
        Intent intent = new Intent(getInstance().getApplicationContext(), SessionTimerService.class);
        intent.setAction(Constant.ACTION_START_SERVICE);
        startService(intent);
    

    public void stopTimer()
        Intent intent = new Intent(getInstance().getApplicationContext(), SessionTimerService.class);
        intent.setAction(Constant.ACTION_STOP_SERVICE);
        stopService(intent);
    

    @Override
    public void onTerminate() 
        super.onTerminate();
    

要在我的活动中启动计时器,我执行以下操作:

App.startTimer()App.stopTimer()

注意:我在活动中调用App.startTimer()

请帮助修复此错误。谢谢你。

【问题讨论】:

【参考方案1】:

如果您的目标是 android 8+,this is an intended behaviour

您的后台服务将在几分钟后被终止。

如果您以 8 之前的版本为目标:

您应该在 Application 类的 onCreate 中使用 startTimer。

如果您将服务设为 Sticky,服务将在终止后重新启动应用程序上下文,并且只执行应用程序类的 onCreate 内的内容。

如果您将 startTimer 保留在 Activity 中,它将在前台按意图启动,并且一旦被杀死就不会重新创建自己。

【讨论】:

我使用最小 SDK 版本 17。我也做 startTimer() 内部活动。但是正如你所说,服务在几分钟后就被杀死了,如何解决这个问题? 或者您可以创建通知并将服务作为前台服务启动 或者如我所说,在 Application 类的 onCreate 中执行 startTimer。一旦你杀死你的应用程序,这将是你保持服务存活的唯一方法 @Flowwlol :那么如何从活动触发到应用程序的onCreate?你能把代码剪掉吗?【参考方案2】:

这取决于你想用你的服务做什么。

如果您希望您的服务在您的应用被终止时运行,这就是将要发生的事情:

应用程序正在运行 -> 被杀死 -> 上下文被破坏 -> 服务在应用程序被杀死时重新启动应用程序,从那里,只有应用程序上下文将处于活动状态,因此您的应用程序中唯一会发生的事情就是 onCreate 中发生的事情应用类。 一旦你杀死你的应用程序,一切都会被破坏,然后它会尝试重新创建它的一部分。

因此,一旦你杀死你的应用程序,它就不会重新创建一个 Activity,也不会重新创建你的服务。

因此,如果您希望在特定 Activity 中启动服务,则一旦您终止应用,该服务将无法工作。

public class App extends Application 

private ApplicationComponent applicationComponent;
private static boolean isChatActivityOpen = false;
private static boolean isSendChat = false;
private static App instanceApp;
private CountDownTimer countDownTimer;
private Handler handler = new Handler();

@Override
public void onCreate() 
    super.onCreate();
    Fabric.with(this, new Crashlytics());

    Timber.plant(new Timber.DebugTree());

    instanceApp = this;

    startTimer();

    // TODO: Add this project to your fabric.io
    /*Crashlytics crashlyticsKit = new Crashlytics.Builder()
            .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
            .build();
    Fabric.with(this, crashlyticsKit);*/

    if (applicationComponent == null) 
        applicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(App.get(this)))
                .build();
    

【讨论】:

startTimer() 放在onCreate 中是很好的解决方案,但它会从头开始重新启动计时器。有什么办法可以让定时器正常运行而不重启? @NiA 您可以使用SharedPref 将计时器状态(即其值)保存在变量中,然后从该特定状态重新启动计时器【参考方案3】:

你可以这样做:

    仅启动服务而不将其绑定到活动 同时启动一个通知服务,即使应用程序被强制关闭,它也会继续运行

【讨论】:

我已经尝试过你的线索,即使我创建了显示计时器的通知,服务仍然强制。 服务什么时候停止?一段时间后或应用程序被强制关闭时? 当应用程序被杀死时,先生。我使用startForeground(1, notification)。我的意思是,当应用程序被终止时,计时器不再运行 一旦服务被强制停止,您可以重新启动服务,您可以使用BroadcastReciever,这是一个链接:***.com/a/21551045/8311441

以上是关于使用 START_STICKY 或 START_NOT_STICKY 保持后台服务不起作用的主要内容,如果未能解决你的问题,请参考以下文章

IntentService 的 START_STICKY

在所有版本的android中以任何模式保持服务活着

Android:后台服务总是关闭

vpp的feature

Android:如果任务管理器杀死,则重新调用应用程序

python2.+进化至python3.+ 语法变动差异(不定期更新)