How to Run Countdowntimer in a Service in Android

How to run CountDownTimer in a Service in Android?

The easiest way is probably to create a broadcast receiver in your activity and have the service send broadcasts to the receiver. Here's a full listing for a service class with a simplified CountDownTimer.

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

And here are the relevant lines from a main activity:

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

You'll also need to define the service between the start/end application tags in your manifest file.

<service android:name=".BroadcastService" />

Countdown timer which runs in the background in android

Add to your AndroidManifest.xml

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

BroadcastReceiver.java

public class BroadcastReceiver extends AppCompatActivity {

TextView tvTimer, tvTimerRunningState, tvTimerFinishedState;
private static final String TAG = "CountdownTimer";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_broadcast_receiver);
}
public void handleStartTimer(View view) {
Intent intent = new Intent(this, BroadcastService.class);
intent.putExtra("inputExtra", "");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ContextCompat.startForegroundService(this, intent);
} else {
this.startService(intent);
}
Log.i(TAG, "timerStarted");
}
public void handleCancelTimer (View view) {
Intent intent = new Intent(this, BroadcastService.class);
stopService(intent);
}
/* CountDown */
final private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateGUI(intent);
}
};

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

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

@Override
public void onStop() {
try {
unregisterReceiver(broadcastReceiver);
} catch (Exception e) {
// Receiver was probably already stopped in onPause()
}
super.onStop();
}

private void updateGUI(Intent intent) {
if (intent.getExtras() != null) {
long millisUntilFinished = intent.getLongExtra("countdown", 0);
long seconds = (millisUntilFinished / 1000) % 60;
long minutes = (millisUntilFinished / (1000*60)) % 60;
long hours = (millisUntilFinished / (1000*60*60)) % 60;
String time = (hours + " : " + minutes + " : " + seconds);
tvTimer = findViewById(R.id.tvTimer);
tvTimer.setText(time);

boolean countdownTimerRunning = intent.getBooleanExtra("countdownTimerRunning", false);
tvTimerRunningState = findViewById(R.id.tvTimerRunningState);
if (countdownTimerRunning) {
tvTimerRunningState.setText("CountdownTimerRunning");
} else {
tvTimer.setText("0 : 0 : 0");
tvTimerRunningState.setText("CountdownTimerNotRunning");
}

boolean countdownTimerFinished = intent.getBooleanExtra("countdownTimerFinished", false);
tvTimerFinishedState = findViewById(R.id.tvTimerFinishedState);
if (countdownTimerFinished) {
tvTimerFinishedState.setText("Finished");
} else {
tvTimerFinishedState.setText("Unfinished");
}
}
}

activity_broadcast_receiver.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/btnStartJob"
android:onClick="handleStartTimer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Timer" />
<Button
android:id="@+id/btnStopJob"
android:onClick="handleCancelTimer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Cancel Timer" />
<TextView
android:id="@+id/tvTimer"
android:text="0 : 0 : 0"
android:gravity="center"
android:textSize="30sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvTimerFinishedState"
android:gravity="center"
android:textSize="20sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvTimerRunningState"
android:gravity="center"
android:textSize="18sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

BroadcastService.java

public class BroadcastService extends Service {

public static final String CHANNEL_ID = "ForegroundServiceChannel";
private final static String TAG = "BroadcastService";
public static final String COUNTDOWN_BR = "your.package.name";
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);
bi.putExtra("countdownTimerRunning", true);
bi.putExtra("countdownTimerFinished", false);
sendBroadcast(bi);
}
@Override
public void onFinish() {
Log.i(TAG, "Timer finished");
bi.putExtra("countdownTimerFinished", true);
sendBroadcast(bi);
stopForeground(true);
stopSelf();
}
}; cdt.start();

}

@Override
public void onDestroy() {
cdt.cancel();
Log.i(TAG, "Timer cancelled");
bi.putExtra("countdownTimerRunning", false);
sendBroadcast(bi);
super.onDestroy();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
/* Notification */
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, BroadcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
/* NotificationBuilder */
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
return START_NOT_STICKY;
}

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

private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}

How to keep a CountDownTimer running even if the app is closed?

Run it in a service as such: use create a broadcast receiver in your activity and have the service send broadcasts.

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

From the main activity:

@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 I got this code from How to run CountDownTimer in a Service in Android?, which I modified for my own android countDownTimer.

How to run my Countdown Timer in Service in Latest API Level?

The right way to schedule tasks based on time or certain constraints (e.g when device is idle, or when it is charging etc) is by using WorkManager. Take a look at it:
https://developer.android.com/topic/libraries/architecture/workmanager/

Specifically, for recurring tasks, have a look at this section:
https://developer.android.com/topic/libraries/architecture/workmanager/basics#recurring



Related Topics



Leave a reply



Submit