Add support for AlarmManager in Timing to allow proper handling of long timers
astreet opened this issue Β· 172 comments
Setting timers for multiple minutes isn't handled properly in React Native on Android: it keeps the Timing module awake instead of relying on the system waking us up when the timer should go off.
We should explore setting a cut off at which we delegate to AlarmManager and Handler.postDelayed instead of handling the timers using framecallbacks.
Great improvement!
this would help with socket.io which keeps a timer of 85000ms
by default. On RN master the threshold is 60000ms
.
And Firebase which is also using long timers.
I am getting following warning while using firebase library
Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info. (Saw setTimeout with duration 111862ms)
How to get rid of this warning...
I am getting following warning while using firebase library too. Somebody know how to solve this problem?
I am also getting this warning with firebase.
"firebase": "^3.9.0",
"react-native": "0.44.0"
Same issue (85000ms
) but without firebase
. My list of packages:
"dependencies": {
"apisauce": "0.11.0",
"format-json": "1.0.3",
"lodash": "4.17.4",
"markdown-it": "^8.3.1",
"native-base": "^2.1.3",
"normalizr": "^3.2.2",
"prop-types": "^15.5.10",
"querystringify": "1.0.0",
"ramda": "0.23.0",
"react": "16.0.0-alpha.6",
"react-markdown": "^2.5.0",
"react-native": "0.44.0",
"react-native-animatable": "1.2.0",
"react-native-config": "0.4.2",
"react-native-device-info": "0.10.2",
"react-native-drawer": "2.3.0",
"react-native-htmlview": "0.9.0",
"react-native-i18n": "1.0.0",
"react-native-linear-gradient": "^2.0.0",
"react-native-photo-view": "^1.2.0",
"react-native-router-flux": "3.39.1",
"react-native-scrollable-tab-view": "*",
"react-native-share": "^1.0.20",
"react-native-vector-icons": "4.1.1",
"react-navigation": "^1.0.0-beta.9",
"react-redux": "5.0.4",
"redux": "3.6.0",
"redux-persist": "4.6.0",
"redux-saga": "0.15.3",
"reduxsauce": "0.4.1",
"seamless-immutable": "7.1.2"
},
"devDependencies": {
"ava": "^0.18.2",
"babel-eslint": "^7.1.1",
"babel-preset-es2015": "^6.18.0",
"enzyme": "^2.6.0",
"husky": "^0.13.1",
"ignite-animatable": "^0.3.1",
"ignite-dev-screens": "^2.0.0-beta.9",
"ignite-i18n": "^0.1.1",
"ignite-ir-boilerplate-2016": "^0.2.2",
"ignite-vector-icons": "^0.2.1",
"mockery": "^2.0.0",
"nyc": "^10.1.2",
"react-addons-test-utils": "^15.3.1",
"react-dom": "^15.4.0",
"react-native-mock": "^0.3.1",
"reactotron-apisauce": "^1.7.0",
"reactotron-react-native": "^1.7.0",
"reactotron-redux": "^1.7.0",
"reactotron-redux-saga": "^1.7.0",
"snazzy": "^6.0.0",
"standard": "^8.6.0"
}
I'm experiencing a similar issue using 0.44.0 on Android. I am also not using firebase:
Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info. (Saw setTimeout with duration 85000ms)
Hi there guys.. was wondering Is there any quick work around for this. I'm using:
react-native 0.44 react 16.0.0-alpha.6 feathers-socketio 1.6.0
Its very annoying popping out while I'm developing.. is there anyway I can hide the warning for now??
Same issue here with firebase 3.9.0
If it bothers you just add console.ignoredYellowBox = ['Setting a timer'];
@skv-headless Opinion incoming. :)
In my opinion that attitude towards problems needs to go away. I've been seeing it pop up more often and it's causing people to ignore warnings. They're warnings. They warn you. Don't ignore them, do something. I've seen apps where all warnings just get ignored, even deprecation notices. Then your app breaks and you're left wondering why.
In this case, you can configure these timeouts and lower them, or setup a different approach of dealing with them. Maybe even poke the maintainers of the libraries and ask them to help come up with a solution.
My advise, is to follow this thread until someone smart comes up with an actual solution, so you can then educate yourself with that answer. And then maybe, in the meantime (if you can't / don't want to teckle the issue) ignore the warnings.
TL;DR;
Yes, you can ignore the warnings temporarily. Just check in every now and then to see what the status is and if there's any action required.
@imamatory I think is by Rectotron
I think is by Rectotron
If so, this warning can be simply ignored.
...however my android emulator getting slow sometimes, maybe it is one of reasons.
@imamatory I hope the real solution is just as easy
Hi, I think I found the solution:
Firstly you have to find the following file in your project: libraries/Core/Timers/JSTimer;js
Open it and you just have to change this const MAX_TIMER_DURATION_MS, to increase above your duration, wrote in the end of warning !
@nicolasZZ hi, Thanks for the solution. but Is that a good practice to modify the JSTimer
library rather than our own code?
@nicolasZZ @AmroAly In my mind this solution is not a good practice because it is very unstable, the best is ignore the alert while the official solution is released.
console.ignoredYellowBox = [
'Setting a timer'
]
the @skv-headless's solution is good but in my case only works with "enter" between of the parenthesis
@rigobcastro Thanks that hides the annoying alerts.
@DZuz14 it would be awesome if you could sent a PR or atleast get the conversation started. Thanks π
hi~
I used socket.io. nodejs and RN44 or RN 45
I saw warning this timers.
I found this solution
reference reactotron PR
personal server nodejs and scoket.io
const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server, {pingTimeout: 30000})
Thanks!
I have a simple react native project for android and I'm using firebase auth with react native for Login and Signup, but I got that yellow error, What should I do? Link of my Question in stackoverslow (https://stackoverflow.com/questions/44603362/setting-a-timer-for-a-long-period-of-time-i-e-multiple-minutes )
Thank You @DZuz14
I Got This Error When I run react-native run-android
D:\Projects 2016\Web\Android\mohajerkade-android\android\app\src\main\java\com\mohajerkade\AlarmManagerModule.java:40: error: cannot find symbol
Intent intent = new Intent(context, AlarmReceiver.class);
^
symbol: class AlarmReceiver
location: class AlarmManagerModule
1 error
:app:compileDebugJavaWithJavac FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
I want to use firebase auth with react native for Login and Signup but I got That yellow error,
1: What is This Module Exactly?
2: Why My Project need This Module?
I asked stackoverflow through this link: (https://stackoverflow.com/questions/44603362/setting-a-timer-for-a-long-period-of-time-i-e-multiple-minutes)
I Reported to Google Firebase Team through this link: (firebase/firebase-js-sdk#97)
Thanks @DZuz14
So if you look into the npm package called firebase
you will find that it uses setTimeout in quite a lot of places. This is OK since the library was written for websites and node.js servers. react-native
is kind of a node.js server but it runs on a phone - and as the warning correctly points out - long running Javascript timers are not good in this environment and might drain the battery and have other unwanted side effects.
So I guess we have to convince the Firebase guys actually to change to your approach and distribute a package made especially for react-native
.
@BerndWessels https://www.npmjs.com/package/react-native-firebase Have you looked at this? Says it allows firebase to run on the native thread. Not sure how true that is, but might be worth a look. You might even be using that, I'm not sure.
@DZuz14 Thanks, I'll check that out.
console.ignoredYellowBox = [
'Setting a timer'
]
@rigobcastro Where can I input or paste this code?
@Jazz747 On the top of your setup file for example app.js, index.android.js or index.ios.js
console.ignoredYellowBox = [ 'Setting a timer' ]
This code only hide the warning inside react native.
How about the warning inside browser console?
Does anyone know if someone from the RN team or the community is working on a solution to this issue?
The warning popup redirects to this issue but I can't find any proper solution here other than to hide the warning, which isn't a solution at all. Any ideas or suggestions?
@SuhairZain Everything seems to indicate that are errors of external libraries to RN
@rigobcastro This warning can be easily reproduced with a setInterval()
call with a time long enough (which was 5 minutes in my case). So I'd say this isn't something caused by 3rd party libs.
BTW, I've since found a library here: react-native-background-timer which helps me get the job done.
@SuhairZain yes u right! my problem was with 3rd party lib but the owner fixed the error (I imagine they just changed some setInterval()
) and the warning disappeared.
Good recommendation, thanks.
i have same problem here. And still couldent understand how should i handle with this problem.
rn: 0.45.1
firebase: 4.1.3
Dear @escalepion I reported to firebase team: (#12981 (comment))
@escalepion If your problem is about setting long running timers, please take a look at my comment above and see if it helps you.
@SuhairZain sorry i couldent understand that how can use setInterval or react-native-background-timer :( where and how will i write the code ? do u have an example?
Does anyone have a tip on how to figure out which library is causing the harm? I recently upgraded RN to 0.46 and suddenly this error appeared. I have quite some libraries in my project and I can only think of investigating them one by one, which obviously isn't ideal.
Cheers!
@liketurbo for sure I will
I'm having this issue alongside with redux-saga
to revalidate the API token every 30 minutes.
Hence, in this case, awaiting for this long is actually correct. And I guess that there are others scenarios which long waiting are expected.
Then my question is, why is this a warning? What can break/happen? Am I not supposed to being able to use any LLT (long lived transactions)?
If LLT are ok. What would be the best approach to handle this issue? Expanding AlarmManager time?
Thanks in advance ;)
I'm having the same issue as @sospedra. I'm using redux-saga
and I have a saga that refreshes some data in the app in the background every 3 minutes, so the saga includes the code yield call(delay, 3 * 60000);
. As a result I'm getting this "Setting a timer for a long period of time..." error.
I'm also wondering what the suggested approach is for something like this. What problems will I run into if I just leave my code working this way. The error message implies there could be performance issues, and perhaps that the timer won't actually get fired at the correct time if the app is in the background?
If there are likely to be problems with this code setting a 3 minute timer, what alternative solution would achieve the same functionality and not run into these problems?
still getting same issue.
I think, the only solution it's move to Native Android Development
The issue still cannot solve? Anyone found the solution?
I am getting warning while using firebase.
If the issue isn't from a 3rd party library, but from you setting a long timeout yourself, a good solution is to use the react-native-background-timer library. It's README contains a simple example as well.
Unfortunately, if the issue is from a 3rd party library, you probably have no solution other than to find the place where the setTimeout is called and raise an issue / submit a PR to use the aforementioned library instead, please try my possible solution below at #12981 (comment) and try if it works for you.
Please make great timeout on android too.
react-native-background-timer is a good workaround as @SuhairZain said. this can clear warning msg
I'm getting this warning when using socket.io
because socket.io has a timer of 85ms
FWIW this only happens to me when I use Reactotron while developing...
Drove me crazy for a while I have to admit but I knew I could not ignore the message
I'm no longer working on the project that I had this issue with, so I can't really test if this approach works, but it should, at least in theory. If the error is from a 3rd party module using setTimeout or setInterval with a long delay, this should be able to fix your issue. What it does is monkey patch the global setTimeout and setInterval to use react-native-background-timer's instead:
import BackgroundTimer from 'react-native-background-timer';
setTimeout = BackgroundTimer.setTimeout;
setInterval = BackgroundTimer.setInterval;
clearTimeout = BackgroundTimer.clearTimeout;
clearInterval = BackgroundTimer.clearInterval;
Write this code somewhere in your early initialization, preferably index.android.js or index.ios.js at the earliest place you can. If someone can test this out, it'd be highly appreciated.
@SuhairZain: your suggestion is working for me so far, with a few tweaks:
setTimeout = BackgroundTimer.setTimeout.bind(BackgroundTimer)
setInterval = BackgroundTimer.setInterval.bind(BackgroundTimer)
clearTimeout = BackgroundTimer.clearTimeout.bind(BackgroundTimer)
clearInterval = BackgroundTimer.clearInterval.bind(BackgroundTimer)
@levity I added your code to my app.js , but I got this
undefined is not an object (evaluating 'RNBackgroundTimer.setTimeout')
@realtebo sounds like you may not have completed the installation of react-native-background-timer as described in their README by either running react-native link
or using CocoaPods.
@levity : do you mean running react-native link
?
npm install -g react-native-cli
react-native link
Scanning folders for symlinks in C:\Users\realtebo\Downloads\manager2\node_modules (54ms)
react-native link
can not be used in Create React Native App projects. If you need to include a library that relies on custom native code, you might have to eject first. See https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md for more information.
I dont't want to eject .... so,.. what can I do?
Oh, sorry, I don't know how to help you with a Create React Native App project. I've never used that myself.
Hi there,
I got some issues with react-native: ^0.49.3
...
cf. ocetnik/react-native-background-timer#65
Hope you can help, thanks for your time ;)
Hi, I have the same issue I am using 0.49
, it appeared after doing this
export function saveUserData(userInfo){
return (dispatch) => {
dispatch(saveDataRequest())
const userId = firebase.auth().currentUser.uid;
return firebase.database().ref('users/' + userId).set(userInfo)
.then(() => {
dispatch(saveDataSuccess());
dispatch(NavigationActions.navigate({ routeName: 'TabContactNavigation' }))
})
.catch((error) => {
dispatch(saveDataFailure(error))
});
}
}
The above is Action
in redux , which also uses redux-thunk
.. When a button is clicked to save data from a form to firebase
when save successful it dispatches saveDataSuccess()
and then navigate to the next tab.
Now I am suspecting the problem to appear to be here const userId = firebase.auth().currentUser.uid
but I am not sure. Here is a screen shot of the problem. (the app acts fine but this warning appears, I dont know if i should simply ignore it) or maybe I am doing something wrong here, need your help guys, thanx
Usually I save into a redux store the userinfo.
So I simply resuse as needed.
... but ... in a similar situation, i've the same identical problem with the timeout
Same issue with react-native-signalr library (300000ms)
socket.io server options
pingTimeout (60000) + pingInterval (25000) <= 60000
Any update on how we should deal with this on Android? Still getting it, not using setTimeout either ...
Just to follow up my previous comment (from August) which I belive sums up the feelings of all the folks around here: we're ok with this AlarmManager thingy is just that we want to know the best approach to handle Long Lived Transactions
A fix for react-native-signalr (ms-signalr-client) on Android is to override default pinginterval (300000):
connection.start({ pingInterval: 5000, jsonp: true, transport: ['webSockets', 'serverSentEvents', 'longPolling']}).done(() => { console.log('Now connected, connection ID =' + connection.id) })
Same issue with react-native-pomelo (80000ms)
console.ignoredYellowBox = ['Setting a timer'];
This is quite safe as a workaround as you will still get the warning in chrome's console when you debug remotely
Where exactly is the best place to put the console ignore code?'
-- nevermind, got a hint from the linked stackexchange, added to my App.js as follows:
class App extends Component {
constructor() {
super();
console.ignoredYellowBox = [
'Setting a timer'
];
}
...
}
and also tried in my index.js as @haikyuu just recommended..
import { AppRegistry } from 'react-native';
import App from './App';
console.ignoredYellowBox = [
'Setting a timer'
];
AppRegistry.registerComponent('manager', () => App);
worked in both
so there isn't any actual solution to this yet? it's been 8+ months now..
?
Let's get this solved then. We'd probably have to figure out:
A.) An easy extensible API that can be called via the React code. I think it would be wise to tackle maybe getting just one of AlarmManagers methods to work. For example, calling the following method in your React Native code would set an alarm with the android devices native AlarmManager.
AlarmMgr.setInexactRepeating(arg1, arg2...)
B.) The actual implementation of getting this to work. As far as I know, setting an alarm with AlarmManager sends what is called a "pending intent" to the AlarmManager service. You tell AlarmManager that you would like to execute some type of "task" in the future. It seems to me, that you would need to have AlarmManager(or a custom React Java Method) send some type of data back to your JavaScript code, telling it what code to execute after the alarm goes off.
Anybody up for tackling this problem? More can be read on AlarmManager here.
btw - is this inefficiency (related to long, multi-minute timeouts) a new issue only for RN versions > 0.43 ? Or is it just that the warning is being spit out from RN > 0.43? I migrated from 0.43.x to 0.47.x and started seeing the warning. I assumed that the inefficiency had always been there, and that RN has just started warning me about that now..
however, ever since the migration a few days ago, I also noticed that my app is consuming a lot more battery on my phone! (and the other features in the diff are not obvious culprits)
Hey @astreet, do you maybe still remember what is that you were referring to when posting this issue and adding this warning here 3637bce479 ?
It has recently became quite annoying to me after I started using one lib that sets a 2min recurring timer so I started to dig into it to understand why this warning has been added in the first place.
Now I am concerned about this sentence: "keeps the Timing module awake". What I think you may be referring to is that Timer module is setting choreographer callback. Although the callback seem to be set regardless of if we have the timer or not. See relevant code part here:
There is no code to stop callback when there are no timers scheduled and there is no code to start scheduling when timer gets added. The callback is suspended only when the app moves to background and restored when moved back to foreground.
As a consequence the choreographer callback is enqueued for each frame regardless of if we have a timer for a long period, short period or no timer at all set. I don't see how setting long timer can impact performance in that case. So does it mean then the warning isn't necessary?
As far as setting long timer being a "correctness issue" I think you are referring to the fact that timers will only be called in the foreground app state (so when you set it for some long time in the future and the app goes into bg it either won't execute, or will execute when the app gets into foreground which may be significantly later that you'd expect). Although I believe this is the same case for iOS where we don't show the warning
In socket.io the default ping loop is 85000ms, = pingInterval(25000ms) + pingTimeout(60000ms). and you should reset those options in server and make sure the sum of pingInterval
and pingTimeout
<= 60000ms.
for example:
var socket = require('socket.io')(12345, {
pingInterval: 20000,
pingTimeout: 40000
})
Just curious - I need to be able to set an approximately 5 minute timeout in my app. How serious is this issue and how much would it affect me?
@ntomallen If the app will be on-screen all the time, then nothing will change. It'll work smoothly. Otherwise, maybe it won't trigger at the end of the 5 minutes. Or it'll do unexactly.
I use laravel echo with React Native on android change socket server options pingTimeout
to 30000. It works
@sospedra gotcha. I'll have to do some testing to make sure it's not too bad!
Is there a reason to not always use Handler.postDelayed ?
any quick fix for this issue ? I just started to use RN with Firebase.
I've tried a few different timer solutions, but haven't had any luck getting one working in the background (consistently).
I've using a setInterval to ping my server every 10 minutes - I just noticed this error message because I do a setInterval(myFunction, 60000)
. Is this something I need to be worried about?
My expected behaviour is that while the app is open, it can send off the requests, but after the app is suspended (single home button press) the requests should stop firing. Is this the behaviour that I can expect, and will the battery life be impacted while the app is suspended?
There's nothing to worry about here, and the warning is incorrect and should be removed. There isn't a "Timing Module" to keep awake, there's just a Choreographer callback that checks for timers every frame (which also makes the min granularity for timers be ~16ms).
There is a correctness issue in the sense that timers won't fire in the background on either iOS or Android. This warning seems to be specifically about Android though and does not appear to be accurate. All timers should eventually fire when the app comes back into the foreground.
Any plans on having this addressed soon? The log is annoying anytime we're testing on Android phones.
My Problemβ¦
I was getting this error because my timeout was up to ~59 minutes. The code to call a method on the top of the hour and had a setTimeout
to create a setInterval
at the next top of the hour.
My Solutionβ¦
- update the
setTimeout
to start thesetInterval
at the top of the minute. - update the function called by the setInterval to only run the function if the current minutes was
0
.
This function now runs every minute instead of every hour⦠but since it does nothing 59 times out of 60, seems harmless.
hey @beausmith, is this really a solution to simply set 60 timers? To my understanding, it is still keeping the timerModule active for the same hour, but isn't showing the warning :)
@beausmith, I tried using https://github.com/ocetnik/react-native-background-timer which uses native timers which sounds like a solution - I've simply overwritten setTimout and setInterval with this lib's implementation.
Supporting timers in background might be a plus or minus depending on your needs :))
Seems to work fine, but didn't solve my problems though :)
I have same warning if I use react-intl FormattedRelative. Because inside component FormattedRelative is used timer:
scheduleNextUpdate(props, state) {
// Cancel and pending update because we're scheduling a new update.
clearTimeout(this._timer);
const {value, units, updateInterval} = props;
const time = new Date(value).getTime();
// If the `updateInterval` is falsy, including `0` or we don't have a
// valid date, then auto updates have been turned off, so we bail and
// skip scheduling an update.
if (!updateInterval || !isFinite(time)) {
return;
}
const delta = time - state.now;
const unitDelay = getUnitDelay(units || selectUnits(delta));
const unitRemainder = Math.abs(delta % unitDelay);
// We want the largest possible timer delay which will still display
// accurate information while reducing unnecessary re-renders. The delay
// should be until the next "interesting" moment, like a tick from
// "1 minute ago" to "2 minutes ago" when the delta is 120,000ms.
const delay =
delta < 0
? Math.max(updateInterval, unitDelay - unitRemainder)
: Math.max(updateInterval, unitRemainder);
this._timer = setTimeout(() => {
this.setState({now: this.context.intl.now()});
}, delay);
}
Here is example of use in my code:
<FormattedRelative value={date}>
{(message: string) => <Text>{message}</Text>}
</FormattedRelative>
Anybody know how fix this warning?
This is still an issue. It's particularly annoying when using firebase services on android. Please fix.
This is a really bad bug for firebase services and when you are dependent on third part apps. Because firebase does not work if you switch between apps to verify something to continue the process.
An example is we use a thirdparty/factor login system, and using firebase to move tokens. But since we clean up the token is gone before the person is back in our app. So I can change the cleanup mechanis, or you guys could fix this bug.
@vongohren not that I understand your use case fully, but one workaround strategy might be to store the backgrounding date within your app on entry to the background, then on return to the foreground compare the current date with the stored backgrounding date and perform some cleanup if a certain time period has elapsed...
To elaborate on my use case. Say you are dependent on saying yes in another app to accept the login. Feks switch over to facebook app to say yes to a login request in a third party. The concrete example im working with is uPort, https://www.uport.me/. Then when we rely on a way to message our app, now in the background to say that all is good, this doesnt work with firebase unless we take your approach to clean up at a later point.
I guess I was just stuck on that the clean up could happen right after emitted information, becaues then the information was already emitted, and we would not need it. But why is it so that the firebase approach does not work in react native? That it stops its background workers.
I need a 30 minutes timeout in my app. Every time the user has interaction with the screen it will reset (throw away old timeout, create new one).
Any alternative on how to do this the right way? Is it better to handle this with native code and pass something to React when finished?
I also getting same warning after using socket.io.
Warning is below:
Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info.
(Saw setTimeout with duration 85000ms)
My package details is below:
- "react": "^16.3.0-alpha.1",
- "react-native": "0.54.4",
- "socket.io": "^2.1.1",
- "socket.io-client": "^2.1.1",
Any permanent solution find out?
I also getting same warning after using socket.io.
Warning is below:
Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info.
(Saw setTimeout with duration 85000ms)
My package details is below:
"react": "^16.3.0-alpha.1",
"react-native": "0.54.4",
"socket.io": "^2.1.1",
"socket.io-client": "^2.1.1",
Any permanent solution find out?