0

I need to implement different CountDownTimers in a recyclerView. I've put a BroadcastService class which contains my CountDownTimer. It broadcasts to my MainActivity, where I'm updating the UI with a timer. However, I need to do this for several different timers with variable initial times. When any of these timers hits zero, some unique code needs to be triggered.

Here's an example of how my BroadcastService class:

package com.example.cdt;

import android.app.Service;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.util.Log;

public class BroadcastService extends Service {

    private final static String TAG = "BroadcastService";

    public static final String COUNTDOWN_BR = "your_package_name.countdown_br";
    Intent bi = new Intent(COUNTDOWN_BR);

    CountDownTimer cdt = null;

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

            Log.i(TAG, "Starting timer...");

            cdt = new CountDownTimer(30000, 1000) {
                @Override
                public void onTick(long millisUntilFinished) {

                    Log.i(TAG, "Countdown seconds remaining: " + millisUntilFinished / 1000);
                    bi.putExtra("countdown", millisUntilFinished);
                    sendBroadcast(bi);
                }

                @Override
                public void onFinish() {
                    Log.i(TAG, "Timer finished");
                }
            };

            cdt.start();
        }

        @Override
        public void onDestroy() {

            cdt.cancel();
            Log.i(TAG, "Timer cancelled");
            super.onDestroy();
        }

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {       
            return super.onStartCommand(intent, flags, startId);
        }

        @Override
        public IBinder onBind(Intent arg0) {       
            return null;
        }
}

My MainActivity then receives the BroadCast Service's timer as such:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    startService(new Intent(this, BroadcastService.class));
    Log.i(TAG, "Started service");
}

private BroadcastReceiver br = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {            
        updateGUI(intent); // or whatever method used to update your GUI fields
    }
};

@Override  
public void onResume() {
    super.onResume();        
    registerReceiver(br, new IntentFilter(BroadcastService.COUNTDOWN_BR));
    Log.i(TAG, "Registered broacast receiver");
    }

@Override
public void onPause() {
    super.onPause();
    unregisterReceiver(br);
    Log.i(TAG, "Unregistered broacast receiver");
}

@Override
public void onStop() {
    try {
        unregisterReceiver(br);
    } catch (Exception e) {
        // Receiver was probably already stopped in onPause()
    }
    super.onStop();
}
@Override
public void onDestroy() {        
    stopService(new Intent(this, BroadcastService.class));
    Log.i(TAG, "Stopped service");
    super.onDestroy();
}

private void updateGUI(Intent intent) {
    if (intent.getExtras() != null) {
        long millisUntilFinished = intent.getLongExtra("countdown", 0);
        Log.i(TAG, "Countdown seconds remaining: " +  millisUntilFinished / 1000);            
    }
}

Note that this code comes from How to run CountDownTimer in a Service in Android? where I learned how to run CountDownTimer as a service.

I have a completely functional RecyclerView. I just don't know how the logic would work using these classes to have several different timers.

For example, if one timer hits 0:00:00, how will Android know what code block to run on the onFinish()?

Community
  • 1
  • 1
segue_segway
  • 1,490
  • 1
  • 22
  • 40

1 Answers1

0

Please note that using broadcast receiver for this type of task may be a very heavy solution and you may consider using some sort of callback interfaces.

Having that said, using your current solution, you can use extra's (bi.putExtra(...)) to add resources to the intents you send. Then inside your receiver you can retrieve those values (get*Extra(...); i.e. int identifiers of the timers - just as you do with bi.putExtra("countdown", millisUntilFinished)).

Edit:

Based on comments, I'm not entirely sure of what you would like to do, but maybe this will help:

The onCreate method of the service will get called only once and should be used for general service initialization - every subsequent call to startService will only cause the onStartCommand invocation, which should be used to create your timers.

When you start your service using startService(new Intent(this, BroadcastService.class)), you can also put extras inside the intent. Then the onStartCommand(Intent intent, int flags, int startId) gets called, with intent having your extras, unique to each startService call.

Inside the onStartCommand you can retrieve extras like identifier of the timer and it's requested runtime, create a CountDownTimer and store them somewhere, e.g. a HashMap, where String key would be the identifier.

Inside the onTick method of each timer, you would also put an antoher value - the timer identifier, which you can retrieve on the application side, so you know which timer posted the broadcast.

This way you can manage the timers on both the app and service side, using your identifier. You may want to handle a situations like starting the service with the same identifier, while the timer is still running (cancellation etc.).

wfranczyk
  • 420
  • 3
  • 12
  • If the timers can be added and deleted by the user at will, how can I utilize int identifiers? – segue_segway Aug 10 '15 at 17:06
  • It does not have to be int identifier - just an example. Check for your options in Intent documentation. – wfranczyk Aug 10 '15 at 17:14
  • I understand your point about using Intents, but what I'm trying to say is this: the user can start different timers and my Broadcast Service class will readily make all the timers. However, once one of the timers runs out or needs to reset, it needs to go back and reference the Broadcast Service class for the values to reset. Maybe in the onPause() or onStop() of my UI, I can send the initial time back to the Broadcast Receiver, where it can then be recreated? – segue_segway Aug 10 '15 at 17:26
  • ^To further elaborate on the above comment, say for example I have several timers, having initial times from 1-10 minutes. When my tenth timer needs to be reset, how will it know to pull a ten minute initial time from the Broadcast Receiver? Also it needs to execute a specific code in its onFinish() that is unique to just that timer – segue_segway Aug 10 '15 at 17:34
  • Going to break the cardinal rule of not posting too many comments and ask a similar question because I feel like I'm being confusing: If I create a single Service class (which creates all my CountDownTimers with different values depending on what value is sent to the service class), and say the first timer reaches the 0:00:00 mark, will the timer be able to go back to the Service class and execute the onFinish() code that was first passed to it? – segue_segway Aug 10 '15 at 17:52
  • Please check the edited answer. As for your last question - why don't you try yourself? Can you see logcat output of "Timer finished" even after you close your app or move it to background? – wfranczyk Aug 10 '15 at 20:55