Open MrShakes opened 3 years ago
Same isssue here !
@MrShakes Did you find a solution?
Same issue
Same issue, did anyone find a way to fix that ? This behaviour was mentioned in issue #28 as well.
same issue .10 milliseconds delay every called
same here about 5s per minute offset :( anyone found a workaround yet?
I attempted to use this for a stopwatch implementation and also experienced this issue. I ended up using a JS implementation instead. In reflection the README of this library does not claim to guarantee accuracy.
@david-gettins hi! Can you describe in two words how you implemented it with JS? I tried to do that using Headless JS, but it seems like it involves some Java coding, and i'm not good at Java:)
@david-gettins hi! Can you describe in two words how you implemented it with JS? I tried to do that using Headless JS, but it seems like it involves some Java coding, and i'm not good at Java:)
Although a JS implementation worked on iOS, on Android it stopped when the app was backgrounded.
I ended up creating my own React Native Native Module, in Kotlin for Android and Swift for iOS. I employed techniques described in the Android developer docs and Apple developer docs to get exact timing and now the solution works with exact second timing and while apps are backgrounded.
Unfortunately this was for an internal project for the company I work full time for. I'll have to ask for permission to publish the code publicly before I share any implementation details.
@mgeorgievsky To answer your question more directly, it was using setTimeout
and correcting the next interval period by diffing the previous fire time with the expected time and adjusting the next millisecond value. If you don't already know setTimeout
and setInterval
are not exact and may fire before or after the specified period.
@mgeorgievsky thanks for your answer! As a result i just did like that: https://github.com/ocetnik/react-native-background-timer/issues/258#issuecomment-749721388
Here a more recent implementation (hook) for a stopwatch. I think that can fix this delay issue (not tested on Android, though):
import {useState, useRef, useEffect} from 'react';
import BackgroundTimer from 'react-native-background-timer';
import {mmkvStorage} from 'App';
const padStart = (num: number) => {
return num.toString().padStart(2, '0');
};
const formatMs = (milliseconds: number) => {
let seconds = ~~(milliseconds / 1000);
let minutes = ~~(seconds / 60);
let hours = ~~(minutes / 60);
minutes = minutes % 60;
seconds = seconds % 60;
const ms = ~~((milliseconds % 1000) / 10);
let str = `${padStart(minutes)}:${padStart(seconds)}.${padStart(ms)}`;
if (hours > 0) {
str = `${padStart(hours)}:${str}`;
}
return str;
};
const enum MMKV_KEYS {
timeWhenLastStopped = 'timeWhenLastStopped',
isRunning = 'isRunning',
startTime = 'startTime',
}
export const useStopWatch = () => {
const [time, setTime] = useState(0);
const [isRunning, setIsRunning] = useState(false);
const [startTime, setStartTime] = useState<number>(0);
const [timeWhenLastStopped, setTimeWhenLastStopped] = useState<number>(0);
const dataLoaded = useRef(false);
useEffect(() => {
// loading persisted data
const timeWhenLastStopped = mmkvStorage.getNumber(
MMKV_KEYS.timeWhenLastStopped,
);
const isRunning = mmkvStorage.getBoolean(MMKV_KEYS.isRunning);
const startTime = mmkvStorage.getNumber(MMKV_KEYS.startTime);
setTimeWhenLastStopped(timeWhenLastStopped ? timeWhenLastStopped : 0);
setIsRunning(!!isRunning);
setStartTime(startTime ? startTime : 0);
dataLoaded.current = true;
}, []);
useEffect(() => {
const persist = () => {
try {
mmkvStorage.set(MMKV_KEYS.timeWhenLastStopped, timeWhenLastStopped);
mmkvStorage.set(MMKV_KEYS.isRunning, isRunning);
mmkvStorage.set(MMKV_KEYS.startTime, startTime);
} catch (e) {
console.log('Error persisting data', e);
}
};
if (dataLoaded.current) persist();
}, [timeWhenLastStopped, isRunning, startTime, dataLoaded.current]);
useEffect(() => {
if (startTime > 0) {
BackgroundTimer.runBackgroundTimer(() => {
setTime(() => Date.now() - startTime + timeWhenLastStopped);
}, 1);
} else {
BackgroundTimer.stopBackgroundTimer();
}
}, [startTime]);
const start = () => {
setIsRunning(true);
setStartTime(Date.now());
};
const pause = () => {
setIsRunning(false);
setStartTime(0);
setTimeWhenLastStopped(time);
};
const reset = () => {
setIsRunning(false);
setStartTime(0);
setTimeWhenLastStopped(0);
setTime(0);
};
return {
start,
pause,
reset,
isRunning,
rawTime: time,
time: formatMs(time),
hasStarted: time > 0,
};
};
For anyone still experiencing this issue (despite the solution above from @hadnet), I solved it with the following:
For iOS it works with JS own setInterval so no additional package needed here. For Android I found the package: https://github.com/juanamd/react-native-background-timer-android that states to be an improvement of this package. I gave it a go and it works perfect. However, in dev mode on real device when returning from background the UI could lag from time to time. But when testing on a production apk, it works smoothly. No delay in time or any other problem. Hopefully this helps anyone still struggling with this problem.
The timer is slower(noticeable within 1 minute) than an actual timer or Javascript's default
setInterval
, I tried usingrunBackgroundTimer
andsetInterval
(for Android), the latter being a bit faster however it would still lag behind an actual timer or Javascript'ssetInterval
. Used on an actual device outside dev.Code(using with Redux):
"react-native-background-timer": "2.4.1" "react-native": "0.61.5"