1

I creating an application using service to running CountDownTimer in Background.

But the background service sometimes failed or stopped by itself when app closed/killed.

I have tried using START_STICKY or START_NOT_STICK but still doesn't work, can't keep the service CountDownTimer alive. (this the different of them)

I also tried all the suggestion that i found on stackoverflow, but again, doesn't work.

Here my code :

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

I start the CountDownTimer from the App. here the code :

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

To start the timer in my Activity, i do as follows :

App.startTimer() or App.stopTimer()

Note : I call App.startTimer() inside an activity.

Please help to fix this bug. I thank you.

Ni A
  • 11
  • 1
  • 3

3 Answers3

0

If you're targeting Android 8+, this is an intended behaviour

Your background service will be killed after few minutes.

If you target a version prior to 8:

You should use the startTimer in the onCreate of the Application class.

If you make your service Sticky, the service will restart the Application context once killed, and only execute what is inside the onCreate of the Application class.

If you keep your startTimer inside an Activity, it will be started as intented in foreground, and once killed won't recreate itself.

Flowwlol
  • 21
  • 2
  • I use min SDK Version 17. A I also do `startTimer()` inside activity. But as you say, the service killed after few minutes, how to solve this? – Ni A Jun 26 '18 at 05:12
  • Or you can create a notification and start the service as a foreground service – MidasLefko Jun 26 '18 at 05:13
  • Or as i said, do the startTimer inside the onCreate of the Application class. That will be the only way for you to keep the Service alive once you kill your app – Flowwlol Jun 26 '18 at 05:33
  • @Flowwlol : Then how to trigger from activity into Application's onCreate? could you give a code snipped? – Ni A Jun 26 '18 at 05:51
0

It depends what you want to do with your Service.

If you want your service to be running when your app is killed, this is what's going to happen :

App running -> Killed -> Context destroyed -> Service restarting Application when it's killed and from there, only the Application Context will be Alive, so the only thing that will happen in your app is what's happening in the onCreate of the Application Class. Once you kill your App, Everything will be destroyed, and then it will try to recreate a part of it.

Therefore, once you kill your app, it will not recreate an Activity, and then not recreate your Service.

So if you want a Service to be started in a specific Activity, this service wont be able to work once you kill your App.

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();
    }
}
Flowwlol
  • 21
  • 2
  • Put the `startTimer()` inside onCreate is good solution, but it restarting the timer from the beginning. Is there any way to keep the timer running normally without restarting? – Ni A Jun 26 '18 at 06:39
  • @NiA You can save the timer state i.e its value in a variable using `SharedPref` and then restart the timer from that particular state – Tabish Jun 26 '18 at 07:36
0

You can do these:

  1. Start service only without binding it to activity
  2. Also start a notification service which will keep running even if the app is force closed
Tabish
  • 390
  • 3
  • 12
  • I have tried your clues, Even i create notification to display the timer, the service still forced. – Ni A Jun 26 '18 at 08:39
  • when do the service stops? after a certain period of time or when the app is force closed? – Tabish Jun 26 '18 at 09:01
  • When the app is killed, sir. I use `startForeground(1, notification)`. I mean, the timer not running anymore when the app is killed – Ni A Jun 26 '18 at 09:03
  • You can restart the service once it is force stopped you can use `BroadcastReciever` for this here is a link: https://stackoverflow.com/a/21551045/8311441 – Tabish Jun 26 '18 at 09:33