Run Timer in 'Background' When App Is Closed

How can I keep a Timer running when the app is closed? (Kotlin)

the best thing you can do is create a Background service.
In this youtube video teach how to make a tamer with a background service
https://www.youtube.com/watch?v=lvibl8YJfGo

And look at this code as a example:

package playstore.com.a02backgroundtimer;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

public class Timer_Service extends Service {

public static String str_receiver = "com.countdowntimerservice.receiver";

private Handler mHandler = new Handler();
Calendar calendar;
SimpleDateFormat simpleDateFormat;
String strDate;
Date date_current, date_diff;
SharedPreferences mpref;
SharedPreferences.Editor mEditor;

private Timer mTimer = null;
public static final long NOTIFY_INTERVAL = 1000;
Intent intent;

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

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

mpref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
mEditor = mpref.edit();
calendar = Calendar.getInstance();
simpleDateFormat = new SimpleDateFormat("HH:mm:ss");

mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 5, NOTIFY_INTERVAL);
intent = new Intent(str_receiver);
}


class TimeDisplayTimerTask extends TimerTask {

@Override
public void run() {
mHandler.post(new Runnable() {

@Override
public void run() {

calendar = Calendar.getInstance();
simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
strDate = simpleDateFormat.format(calendar.getTime());
Log.e("strDate", strDate);
twoDatesBetweenTime();

}

});
}

}

public String twoDatesBetweenTime() {


try {
date_current = simpleDateFormat.parse(strDate);
} catch (Exception e) {

}

try {
date_diff = simpleDateFormat.parse(mpref.getString("data",""));
} catch (Exception e) {

}

try {


long diff = date_current.getTime() - date_diff.getTime();
int int_hours = Integer.valueOf(mpref.getString("hours", ""));

long int_timer = TimeUnit.HOURS.toMillis(int_hours);
long long_hours = int_timer - diff;
long diffSeconds2 = long_hours / 1000 % 60;
long diffMinutes2 = long_hours / (60 * 1000) % 60;
long diffHours2 = long_hours / (60 * 60 * 1000) % 24;


if (long_hours > 0) {
String str_testing = diffHours2 + ":" + diffMinutes2 + ":" + diffSeconds2;

Log.e("TIME", str_testing);

fn_update(str_testing);
} else {
mEditor.putBoolean("finish", true).commit();
mTimer.cancel();
}
} catch (Exception e) {
mTimer.cancel();
mTimer.purge();


}

return "";

}

@Override
public void onDestroy() {
super.onDestroy();
Log.e("Service finish", "Finish");
}

private void fn_update(String str_time) {

intent.putExtra("time", str_time);
sendBroadcast(intent);
}
}

Run Timer in Backgrounf in flutter

Thank you for giving answer

But I find solution like current time save in local device using SharedPreferences and when user open app again then find difference between
saved time and currenttime show in my app

here is my code

    startTimer() {
timerStream = stopWatchStream();
timerSubscription = timerStream.listen((int newTick) {
String time = _sharedPreferences.getString("time");
Duration duration = DateTime.now().difference(DateTime.parse(time));

setState(() {
hoursStr = ((duration.inSeconds / (60 * 60)) % 60)
.floor()
.toString()
.padLeft(2, '0');
minutesStr =
((duration.inSeconds / 60) % 60).floor().toString().padLeft(2, '0');
secondsStr =
(duration.inSeconds % 60).floor().toString().padLeft(2, '0');
});
});
}

CountDownTimer problem when app is closed

UPDATED

Below is your code converted into a code snippet for a CountdownTimer which will keep working even when the app is closed, pushed to background or restarted.

set START_TIME_IN_MILLIS as the Timer start time, in the following example it is set to 15 seconds.

import android.content.SharedPreferences;
import android.os.CountDownTimer;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.Locale;

public class MainActivity2 extends AppCompatActivity {
private static final long START_TIME_IN_MILLIS = 15000;
private TextView mTextViewCountDown;
private CountDownTimer mCountDownTimer;
private boolean mTimerRunning;
private long mTimeLeftInMillis;
private long mEndTime;
private long remainingTimeInMillis;

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

mTextViewCountDown = findViewById(R.id.tv);
}

private void startTimer() {
mCountDownTimer = new CountDownTimer(remainingTimeInMillis, 1000) {
@Override
public void onTick(long millisUntilFinished) {
remainingTimeInMillis = millisUntilFinished;
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}

@Override
public void onFinish() {
//mTimerRunning = false;
//updateButtons();

updateCountDownText();
resetTimer();
startTimer();

}
}.start();

//mTimerRunning = true;

}


private void resetTimer() {
remainingTimeInMillis = START_TIME_IN_MILLIS;
updateCountDownText();

}

private void updateCountDownText() {


int minutes = (int) (remainingTimeInMillis / 1000) / 60;
int seconds = (int) (remainingTimeInMillis / 1000) % 60;

String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);

mTextViewCountDown.setText(timeLeftFormatted);
}


@Override
protected void onStop() {
super.onStop();

SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();

editor.putLong("millisLeft", mTimeLeftInMillis);
editor.putBoolean("timerRunning", mTimerRunning);
editor.putLong("endTime", System.currentTimeMillis());
editor.apply();

}

@Override
protected void onStart() {
super.onStart();

SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);

mTimeLeftInMillis = prefs.getLong("millisLeft", START_TIME_IN_MILLIS);
mTimerRunning = prefs.getBoolean("timerRunning", false);
mEndTime = prefs.getLong("endTime", 0);
if (mEndTime == 0L) {
remainingTimeInMillis = (mTimeLeftInMillis);
} else {
Long timeDiff = (mEndTime - System.currentTimeMillis());
//to convert into positive number
timeDiff = Math.abs(timeDiff);

long timeDiffInSeconds = (timeDiff / 1000) % 60;
long timeDiffInMillis = timeDiffInSeconds * 1000;
Long timeDiffInMillisPlusTimerRemaining = remainingTimeInMillis = mTimeLeftInMillis - timeDiffInMillis;

if (timeDiffInMillisPlusTimerRemaining < 0) {
timeDiffInMillisPlusTimerRemaining = Math.abs(timeDiffInMillisPlusTimerRemaining);
remainingTimeInMillis = START_TIME_IN_MILLIS - timeDiffInMillisPlusTimerRemaining;
}
}
updateCountDownText();
startTimer();
}
}

Run timer in 'background' when app is closed

The correct approach is to maintain the illusion that the timer is running, without actually keeping it running. There are two aspects to this:

  1. Before the app leaves the foreground, it should save information about the timer (e.g. either the time the timer started if you're counting up or the time the timer is supposed to stop if you're counting down). You can use a variety of different technologies to save this information (e.g. a plist, keyed archiver, Core Data, etc.) in persistent storage.

    Then when the app starts up again, check the saved time and reconstruct the UI to start the timer again from the appropriate state.

  2. If you're counting down to some time (i.e. you want to let the user know when the particular point in time has been reached, even if the app isn't running), you could use a local notification. See the Local and Remote Notification Programming Guide, focusing on the local notifications.

HOW TO KEEP TIMER RUNNING WHEN CLOSING THE APP (Background) IN A FRAGMENT

For running tasks after app is killed consider using background services in android

How to keep the timer running after the view/app is closed?

To enable a Timer func to run in the background you can mark the Audio, AirPlay, and Picture in Picture option on Background Modes on your Signing & Capabilities on your App's target.

Now, this is only valid when the app is on the background (not closed), it is not possible to perform any task when your app are closed. A workaround would be to store the date/time of when your app was last opened and do the proper calculations.

This can clarify more about background execution.

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.

Flutter: run code like a timer when app is running in background

Try the below code -

I tested it & found it will count the number in the background & there is no problem.


I added a screen record video here, it will help you to understand.

import 'dart:async';
import 'package:flutter/material.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyTest(),
);
}
}

class MyTest extends StatefulWidget {
const MyTest({Key? key}) : super(key: key);

@override
State<MyTest> createState() => _MyTestState();
}

class _MyTestState extends State<MyTest> {
final SetTimer _setTimer = SetTimer();

@override
void initState() {
_setTimer.start();
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<int>(
stream: _setTimer.stream,
builder: (
BuildContext context,
AsyncSnapshot<int> snapshot,
) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.active
|| snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Error');
} else if (snapshot.hasData) {
return Center(
child: Text(
snapshot.data.toString(),
style: const TextStyle(color: Colors.red, fontSize: 40)
),
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
),
);
}
}

class SetTimer {
int _seconds = 0;
final _streamController = StreamController<int>.broadcast();
Timer? _timer;

// Getters
Stream<int> get stream => _streamController.stream;

// Setters
void start() {
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
_seconds++;
_updateSeconds();
});
}

void _updateSeconds() {
// stop counting after one hour
if (_seconds < 3600) {
_streamController.sink.add(_seconds);
}
}
}


Related Topics



Leave a reply



Submit