React-native: Adicione suporte para AlarmManager em Timing para permitir o manuseio adequado de temporizadores longos

Criado em 16 mar. 2017  ·  173Comentários  ·  Fonte: facebook/react-native

Configurar temporizadores para vários minutos não é tratado corretamente no React Native no Android: ele mantém o módulo Timing ativo em vez de depender do sistema nos acordando quando o temporizador deveria desligar.

Devemos explorar a definição de um corte no qual delegamos a AlarmManager e Handler.postDelayed em vez de manipular os temporizadores usando framecallbacks.

Bug JavaScript Android

Comentários muito úteis

@ Skv-headless Opinião de entrada. :)

Em minha opinião, essa atitude em relação aos problemas precisa desaparecer. Tenho visto isso aparecer com mais frequência e está fazendo com que as pessoas ignorem os avisos. Eles são avisos. Eles avisam você. Não os ignore, faça algo. Já vi aplicativos em que todos os avisos são ignorados, até mesmo avisos de depreciação. Em seguida, seu aplicativo quebra e você fica se perguntando o porquê.

Nesse caso, você pode configurar esses tempos limite e reduzi-los ou definir uma abordagem diferente para lidar com eles. Talvez até cutuque os mantenedores das bibliotecas e peça-lhes que ajudem a encontrar uma solução.

Meu conselho é seguir este tópico até que alguém inteligente encontre uma solução real, para que você possa se instruir com essa resposta. E então talvez, enquanto isso (se você não puder / não quiser discutir o problema), ignore os avisos.

TL; DR;

Sim, você pode ignorar os avisos temporariamente. Basta verificar de vez em quando para ver qual é o status e se há alguma ação necessária.

Todos 173 comentários

Grande melhoria!

isso ajudaria com socket.io, que mantém um cronômetro de 85000ms por padrão. No mestre RN, o limite é 60000ms .

E o Firebase, que também usa longos temporizadores.

Estou recebendo o seguinte aviso ao usar a biblioteca 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 111862ms)

Como se livrar deste aviso ...

Estou recebendo o seguinte aviso ao usar a biblioteca Firebase também. Alguém sabe como resolver esse problema?

Também estou recebendo este aviso com o firebase.

"firebase": "^3.9.0",
"react-native": "0.44.0"

Mesmo problema ( 85000ms ), mas sem firebase . Minha lista de pacotes:

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

Estou enfrentando um problema semelhante ao usar o 0.44.0 no Android. Também não estou usando o 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)

Olá pessoal .. estava me perguntando se existe alguma solução rápida para isso. Estou a usar:

react-native 0.44 react 16.0.0-alpha.6 feathers-socketio 1.6.0

É muito chato aparecer enquanto estou desenvolvendo ... há alguma maneira de esconder o aviso por enquanto?

O mesmo problema aqui com o firebase 3.9.0

Se incomodar, basta adicionar console.ignoredYellowBox = ['Setting a timer'];

@ Skv-headless Opinião de entrada. :)

Em minha opinião, essa atitude em relação aos problemas precisa desaparecer. Tenho visto isso aparecer com mais frequência e está fazendo com que as pessoas ignorem os avisos. Eles são avisos. Eles avisam você. Não os ignore, faça algo. Já vi aplicativos em que todos os avisos são ignorados, até mesmo avisos de depreciação. Em seguida, seu aplicativo quebra e você fica se perguntando o porquê.

Nesse caso, você pode configurar esses tempos limite e reduzi-los ou definir uma abordagem diferente para lidar com eles. Talvez até cutuque os mantenedores das bibliotecas e peça-lhes que ajudem a encontrar uma solução.

Meu conselho é seguir este tópico até que alguém inteligente encontre uma solução real, para que você possa se instruir com essa resposta. E então talvez, enquanto isso (se você não puder / não quiser discutir o problema), ignore os avisos.

TL; DR;

Sim, você pode ignorar os avisos temporariamente. Basta verificar de vez em quando para ver qual é o status e se há alguma ação necessária.

@imamatory eu acho que é da Rectotron

Eu acho que é da Rectotron

Nesse caso, esse aviso pode ser simplesmente ignorado.
... no entanto meu emulador de Android fica lento às vezes, talvez seja um dos motivos.

@imamation Espero que a solução real seja tão fácil

Olá, acho que encontrei a solução:
Em primeiro lugar, você deve encontrar o seguinte arquivo em seu projeto: libraries / Core / Timers / JSTimer; js
Abra-o e você só tem que mudar este const MAX_TIMER_DURATION_MS, para aumentar acima de sua duração, escreveu no final do aviso!

@nicolasZZ oi, Obrigado pela solução. mas é uma boa prática modificar a biblioteca JSTimer vez de nosso próprio código?

@nicolasZZ @AmroAly Na minha opinião esta solução não é uma boa prática porque é muito instável, o melhor é ignorar o alerta enquanto a solução oficial é lançada.

console.ignoredYellowBox = [
    'Setting a timer'
]

a solução do @ skv-headless é boa, mas no meu caso só funciona com "enter" entre os parênteses

@rigobcastro Obrigado que esconde os alertas irritantes.

@ DZuz14 seria incrível se você pudesse enviar um PR ou pelo menos iniciar a conversa. Obrigado 👍

oi ~
Eu usei socket.io. nodejs and RN44 or RN 45
Eu vi um aviso neste temporizador.

Eu encontrei esta solução
reference reactotron PR

nodejs de servidor pessoal e scoket.io

const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server, {pingTimeout: 30000})

Obrigado!

Eu tenho um projeto react native simples para android e estou usando o firebase auth com react native para login e inscrição, mas recebi aquele erro amarelo, o que devo fazer? Link da minha pergunta em stackoverslow (https://stackoverflow.com/questions/44603362/setting-a-timer-for-a-long-period-of-time-ie-multiple-minutes)

Obrigado @ DZuz14
Recebi este erro quando executo 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.

Quero usar o firebase auth com react native para login e inscrição, mas recebi aquele erro amarelo,
1: O que é exatamente este módulo?
2: Por que meu projeto precisa deste módulo?

Solicitei stackoverflow por meio deste link: (https://stackoverflow.com/questions/44603362/setting-a-timer-for-a-long-period-of-time-ie-multiple-minutes)
Reportei à equipe do Google Firebase por meio deste link: (https://github.com/firebase/firebase-js-sdk/issues/97)

@ DZuz14 Eu tenho a mesma pergunta que @saeedhei .

Basicamente, estamos usando o módulo de nó firebase que usa esses cronômetros de longo período.

Adicionar seu módulo não é suficiente, eu acho, na verdade temos que reescrever o código dentro do módulo firebase node para usar seus alarmes em vez de timers Javascript, certo?

Obrigado @ DZuz14

Então, se você olhar para o pacote npm chamado firebase você descobrirá que ele usa setTimeout em vários lugares. Isso está OK, pois a biblioteca foi escrita para sites e servidores node.js. react-native é uma espécie de servidor node.js, mas funciona em um telefone - e como o aviso aponta corretamente - os temporizadores Javascript de longa duração não são bons neste ambiente e podem esgotar a bateria e ter outros efeitos colaterais indesejados .

Portanto, acho que temos que convencer o pessoal do Firebase a mudar sua abordagem e distribuir um pacote feito especialmente para react-native .

@BerndWessels https://www.npmjs.com/package/react-native-firebase Você já olhou isso? Diz que permite que o firebase seja executado no thread nativo. Não tenho certeza se isso é verdade, mas vale a pena dar uma olhada. Você pode até estar usando isso, não tenho certeza.

@ DZuz14 Obrigado, vou verificar isso.

console.ignoredYellowBox = [
'Ajustando um cronômetro'
]

@rigobcastro Onde posso inserir ou colar este código?

@ Jazz747 No topo de seu arquivo de configuração, por exemplo app.js, index.android.js ou index.ios.js

console.ignoredYellowBox = [ 'Setting a timer' ]
Este código apenas oculta o aviso dentro da reação nativa.

Que tal o aviso dentro do console do navegador?

Alguém sabe se alguém da equipe RN ou da comunidade está trabalhando em uma solução para esse problema?

O pop-up de aviso redireciona para esse problema, mas não consigo encontrar nenhuma solução adequada aqui, a não ser ocultar o aviso, o que não é uma solução. Alguma ideia ou sugestão?

@SuhairZain Tudo parece indicar que são erros de bibliotecas externas para RN

@rigobcastro Este aviso pode ser facilmente reproduzido com uma chamada setInterval() com um tempo suficiente (que foi de 5 minutos no meu caso). Então, eu diria que isso não é algo causado por libs de terceiros.

BTW, encontrei uma biblioteca aqui: react-native-background-timer que me ajuda a fazer o trabalho.

@SuhairZain sim, você está certo! meu problema era com lib de terceiros, mas o proprietário corrigiu o erro (imagino que eles mudaram apenas setInterval() ) e o aviso desapareceu.

Boa recomendação, obrigado.

eu tenho o mesmo problema aqui. E ainda não consegui entender como devo lidar com esse problema.

rn: 0,45,1
firebase: 4.1.3

Caro @escalepion , relatei à equipe do firebase: (https://github.com/facebook/react-native/issues/12981#issuecomment-309595327)

@escalepion Se o seu problema é definir temporizadores de longa duração, por favor, dê uma olhada no meu comentário acima e veja se ele ajuda você.

@SuhairZain desculpe, eu

Alguém tem uma dica de como descobrir qual biblioteca está causando o mal? Recentemente, atualizei RN para 0,46 e de repente esse erro apareceu. Tenho algumas bibliotecas em meu projeto e só consigo pensar em investigá-las uma por uma, o que obviamente não é o ideal.

Felicidades!

@ abeltje1 , se você descobrir como resolver este problema, por favor, atualize aqui

@liketurbo com certeza vou

Estou tendo esse problema junto com redux-saga para revalidar o token de API a cada 30 minutos.

Portanto, neste caso, aguardar esse tempo é realmente correto. E eu acho que existem outros cenários que se esperam de longa espera.

Então minha pergunta é: por que isso é um aviso? O que pode quebrar / acontecer? Não deveria ser capaz de usar qualquer LLT (transações de longa duração)?

Se o LLT estiver ok. Qual seria a melhor abordagem para lidar com esse problema? Expandindo o tempo do AlarmManager?

Desde já, obrigado ;)

Estou tendo o mesmo problema que @sospedra. Estou usando redux-saga e tenho uma saga que atualiza alguns dados do aplicativo em segundo plano a cada 3 minutos, então a saga inclui o código yield call(delay, 3 * 60000); . Como resultado, estou recebendo o erro "Configurando um cronômetro por um longo período de tempo ...".

Também estou me perguntando qual é a abordagem sugerida para algo assim. Que problemas terei de enfrentar se apenas deixar meu código funcionando dessa maneira. A mensagem de erro indica que pode haver problemas de desempenho e talvez o cronômetro não seja acionado no momento correto se o aplicativo estiver em segundo plano.

Se houver probabilidade de haver problemas com esse código ao definir um cronômetro de 3 minutos, que solução alternativa teria a mesma funcionalidade e não teria esses problemas?

ainda tendo o mesmo problema.

Acho que a única solução é mudar para o desenvolvimento Android nativo

O problema ainda não resolve? Alguém encontrou a solução?

Estou recebendo um aviso ao usar o firebase.

Se o problema não for de uma biblioteca de terceiros, mas de você mesmo definir um longo tempo limite, uma boa solução é usar a biblioteca react-native-background-timer . Seu README também contém um exemplo simples.

Infelizmente , se o problema for de uma biblioteca de terceiros, você provavelmente não terá outra solução a não ser encontrar o local onde o setTimeout é chamado e levantar um problema / enviar um PR para usar a biblioteca mencionada , tente minha possível solução abaixo em https://github.com/facebook/react-native/issues/12981#issuecomment -331074444 e tente se funciona para você.

Faça um ótimo tempo limite no Android também.

react-native-background-timer é uma boa solução, como disse @SuhairZain . isso pode limpar a mensagem de aviso

Estou recebendo este aviso ao usar socket.io

porque socket.io tem um temporizador de 85ms

FWIW isso só acontece comigo quando eu uso Reactotron durante o desenvolvimento ...

Me deixou louco por um tempo, tenho que admitir, mas sabia que não poderia ignorar a mensagem

Não estou mais trabalhando no projeto com o qual tive esse problema, então não posso realmente testar se essa abordagem funciona, mas deveria, pelo menos em teoria. Se o erro for de um módulo de terceiros usando setTimeout ou setInterval com um longo atraso, isso deve ser capaz de corrigir o problema. O que ele faz é fazer um monkey patch ao setTimeout global e setInterval para usar o react-native-background-timer 's em seu lugar:

import BackgroundTimer from 'react-native-background-timer';

setTimeout = BackgroundTimer.setTimeout;
setInterval = BackgroundTimer.setInterval;
clearTimeout = BackgroundTimer.clearTimeout;
clearInterval = BackgroundTimer.clearInterval;

Escreva este código em algum lugar em sua inicialização, de preferência index.android.js ou index.ios.js o mais cedo possível. Se alguém puder testar isso, será muito apreciado.

@SuhairZain : sua sugestão está funcionando para mim até agora, com alguns ajustes:

setTimeout = BackgroundTimer.setTimeout.bind(BackgroundTimer)
setInterval = BackgroundTimer.setInterval.bind(BackgroundTimer)
clearTimeout = BackgroundTimer.clearTimeout.bind(BackgroundTimer)
clearInterval = BackgroundTimer.clearInterval.bind(BackgroundTimer)

@levity adicionei seu código ao meu app.js, mas entendi

undefined não é um objeto (avaliando 'RNBackgroundTimer.setTimeout')

@realtebo parece que você pode não ter concluído a instalação do react-native-background-timer conforme descrito em seu README executando react-native link ou usando CocoaPods.

@levity : você quer dizer correr react-native link ?

npm install -g react-native-cli
link react-nativo
Verificando pastas em busca de links simbólicos em C: \ Users \ realtebo \ Downloads \ manager2 \ node_modules (54ms)
react-native link não pode ser usado em projetos Create React Native App. Se você precisar incluir uma biblioteca que dependa de código nativo personalizado, pode ser necessário ejetar primeiro. Consulte https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md para obter mais informações.

Eu não quero ejetar .... então, .. o que posso fazer?

Oh, desculpe, não sei como ajudá-lo com um projeto Create React Native App. Eu nunca usei isso.

Olá,
Tive alguns problemas com react-native: ^0.49.3 ...

cf. https://github.com/ocetnik/react-native-background-timer/issues/65

Espero que você possa ajudar, obrigado pelo seu tempo;)

Olá, tenho o mesmo problema que estou usando 0.49 , ele apareceu depois de fazer isso

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

O acima é Action em redux, que também usa redux-thunk .. Quando um botão é clicado para salvar dados de um formulário para firebase quando salva com sucesso, ele despacha saveDataSuccess() e navegue para a próxima guia.
Agora estou suspeitando que o problema parece estar aqui const userId = firebase.auth().currentUser.uid mas não tenho certeza. Aqui está uma captura de tela do problema. (o aplicativo funciona bem, mas aparece este aviso, não sei se devo simplesmente ignorá-lo) ou talvez eu esteja fazendo algo errado aqui, preciso de sua ajuda, pessoal, obrigado

warning

warning2

hey, o aviso é sobre setTimeout você está usando em seu componente?
e sim, a linha const userId = firebase.auth (). currentUser.uid; não está funcionando, isso é uma promessa e quando você usa userId ele ainda não vem.
a melhor maneira de fazer isso é obter o userId do actionCreator, você já está enviando a variável userInfo, tente isto:

presumirei que userInfo vem do login de autenticação do firebase, então você tem o uid nele

função de exportação saveUserData (userInfo) {
retorno (despacho) => {
despachar (saveDataRequest ())
// const userId = firebase.auth (). currentUser.uid;
return firebase.database (). ref ('users /' + userInfo.uid) .set (userInfo)
.então (() => {
despachar (saveDataSuccess ());
despacho (NavigationActions.navigate ({routeName: 'TabContactNavigation'}))
})
.catch ((erro) => {
despachar (saveDataFailure (erro))
});
}
}
espero que isso possa ajudar, e desculpe pelo meu inglês ruim = P

Felicidade

El 23/10/2017, a las 23:59, Yasir [email protected] escribió:

Olá, tenho o mesmo problema que estou usando 0,49, ele apareceu depois de fazer isso

função de exportação saveUserData (userInfo) {
retorno (despacho) => {
despachar (saveDataRequest ())
const userId = firebase.auth (). currentUser.uid;
return firebase.database (). ref ('users /' + userId) .set (userInfo)
.então (() => {
despachar (saveDataSuccess ());
despacho (NavigationActions.navigate ({routeName: 'TabContactNavigation'}))
})
.catch ((erro) => {
despachar (saveDataFailure (erro))
});
}
}
Acima é Action in redux, que também usa redux-thunk. Quando um botão é clicado para salvar dados de um formulário no firebase, quando o salvamento é bem-sucedido, ele envia saveDataSuccess () e, em seguida, navega para a próxima guia.
Agora estou suspeitando que o problema parece estar aqui const userId = firebase.auth (). CurrentUser.uid, mas não tenho certeza. Aqui está uma captura de tela do problema. (o aplicativo funciona bem, mas aparece este aviso, não sei se devo simplesmente ignorá-lo) ou talvez eu esteja fazendo algo errado aqui, preciso de sua ajuda, pessoal, obrigado

https://user-images.githubusercontent.com/12644122/31925375-94b7c464-b89d-11e7-889d-e9d00866ff73.png
https://user-images.githubusercontent.com/12644122/31925386-9e9bf3c4-b89d-11e7-9eaa-65147879629f.png
-
Você está recebendo isto porque está inscrito neste tópico.
Responda a este e-mail diretamente, visualize-o no GitHub https://github.com/facebook/react-native/issues/12981#issuecomment-338874018 ou ignore o tópico https://github.com/notifications/unsubscribe-auth/ AHhM1oYWKrWSOZsm-8hjp4Q6qJSfqraUks5svW6-gaJpZM4Mf0Z0 .

Normalmente eu salvo em um redux store o userinfo.
Então, simplesmente reutilizo conforme necessário.
... mas ... em uma situação semelhante, tenho o mesmo problema idêntico com o tempo limite

O mesmo problema com a biblioteca react-native-signalr (300000ms)

opções do servidor socket.io
pingTimeout (60000) + pingInterval (25000) <= 60000

Alguma atualização sobre como devemos lidar com isso no Android? Ainda entendendo, sem usar setTimeout também ...

Apenas para acompanhar meu comentário anterior (de agosto), que eu acredito que resume os sentimentos de todas as pessoas por aqui: estamos bem com esse AlarmManager é apenas que queremos saber a melhor abordagem para lidar com Transações de Longa Vida

cc @shergin @cpojer @javache @hramos

Uma correção para react-native-signalr (ms-signalr-client) no Android é substituir o pinginterval padrão (300000):
connection.start({ pingInterval: 5000, jsonp: true, transport: ['webSockets', 'serverSentEvents', 'longPolling']}).done(() => { console.log('Now connected, connection ID =' + connection.id) })

Mesmo problema com react-native-pomelo (80000ms)

console.ignoredYellowBox = ['Setting a timer'];

Isso é bastante seguro como uma solução alternativa, pois você ainda receberá o aviso no console do Chrome quando depurar remotamente

Onde exatamente é o melhor lugar para colocar o código de ignorar do console? '

- Não importa, tenho uma dica do ligada Stackexchange , acrescentou aos meus app.js da seguinte forma:

class App extends Component {
  constructor() {
    super();

    console.ignoredYellowBox = [
      'Setting a timer'
    ];
  }
...
}
````

and also tried in my index.js as <strong i="8">@haikyuu</strong> just recommended..

importar {AppRegistry} de 'react-native';
importar aplicativo de './App';

console.ignoredYellowBox = [
'Ajustando um cronômetro'
];

AppRegistry.registerComponent ('gerente', () => App);
`` ``

trabalhou em ambos

@CopyJosh você pode colocá-lo em seu arquivo de entrada: index.js

então ainda não existe uma solução real para isso? já se passaram mais de 8 meses ..

?

Vamos resolver isso então. Provavelmente teríamos que descobrir:

A.) Uma API facilmente extensível que pode ser chamada por meio do código React. Eu acho que seria sábio tentar fazer apenas um dos métodos AlarmManagers funcionar. Por exemplo, chamar o método a seguir em seu código React Native definiria um alarme com o AlarmManager nativo dos dispositivos Android.
AlarmMgr.setInexactRepeating(arg1, arg2...)

B.) A implementação real para fazer isso funcionar. Pelo que eu sei, definir um alarme com AlarmManager envia o que é chamado de "intenção pendente" para o serviço AlarmManager. Você diz ao AlarmManager que gostaria de executar algum tipo de "tarefa" no futuro. Parece-me que você precisaria que o AlarmManager (ou um método Java React customizado) enviasse algum tipo de dado de volta ao seu código JavaScript, informando qual código executar depois que o alarme disparar.

Alguém está disposto a resolver este problema? Mais pode ser lido no AlarmManager aqui .

btw - esta ineficiência (relacionada a longos tempos limite de vários minutos) é um novo problema apenas para versões RN> 0,43? Ou será apenas que o aviso está sendo cuspido de RN> 0,43? Migrei de 0.43.x para 0.47.xe comecei a ver o aviso. Presumi que a ineficiência sempre existiu, e que o RN começou a me alertar sobre isso agora.

no entanto, desde a migração, alguns dias atrás, também percebi que meu aplicativo está consumindo muito mais bateria do meu telefone! (e os outros recursos no diff não são culpados óbvios)

Ei, @astreet , talvez você ainda se lembre a que se referia ao postar este problema e adicionar este aviso aqui https://github.com/facebook/react-native/commit/3637bce479 ?

Recentemente, tornou-se muito irritante para mim depois que comecei a usar uma lib que define um cronômetro recorrente de 2 minutos, então comecei a me aprofundar para entender por que esse aviso foi adicionado em primeiro lugar.

Agora estou preocupado com esta frase: "mantém o módulo Timing ativo". O que eu acho que você pode estar se referindo é que o módulo Timer está configurando o retorno de chamada do coreógrafo. Embora o retorno de chamada pareça estar definido, independentemente de termos ou não o cronômetro. Veja a parte do código relevante aqui: https://github.com/facebook/react-native/blob/1e8f3b11027fe0a7514b4fc97d0798d3c64bc895/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java#L106
Não há código para interromper o retorno de chamada quando não há cronômetros programados e não há código para iniciar a programação quando o cronômetro é adicionado. O retorno de chamada é suspenso apenas quando o aplicativo passa para o segundo plano e restaurado quando é movido de volta para o primeiro plano.

Como consequência, o retorno de chamada do coreógrafo é enfileirado para cada quadro, independentemente de termos um temporizador para um longo período, curto período ou nenhum temporizador definido. Não vejo como definir o temporizador longo pode afetar o desempenho nesse caso. Então, isso significa que o aviso não é necessário?

Quanto a definir o longo timer ser um "problema de correção", acho que você está se referindo ao fato de que os temporizadores só serão chamados no estado do aplicativo em primeiro plano (então, quando você configurá-lo para um longo tempo no futuro e o aplicativo entrar em bg ele não será executado ou será executado quando o aplicativo entrar em primeiro plano, o que pode ser significativamente mais tarde do que você esperava). Embora eu acredite que este seja o mesmo caso para iOS, onde não mostramos o aviso

No socket.io, o loop de ping padrão é 85000ms, = pingInterval (25000ms) + pingTimeout (60000ms). e você deve redefinir essas opções no servidor e certificar-se da soma de pingInterval e pingTimeout <= 60000ms.

por exemplo:

var socket = require('socket.io')(12345, {
  pingInterval: 20000, 
  pingTimeout: 40000
})

Só por curiosidade - preciso definir um tempo limite de aproximadamente 5 minutos no meu aplicativo. Quão sério é esse problema e quanto isso me afetaria?

@ntomallen Se o aplicativo ficar na tela o tempo todo, nada mudará. Vai funcionar bem. Caso contrário, talvez ele não seja acionado ao final dos 5 minutos. Ou acontecerá inesperadamente.

Eu uso o laravel echo com React Native no Android, altere as opções do servidor de soquete pingTimeout para 30000. Funciona

@sospedra gotcha. Vou ter que fazer alguns testes para ter certeza de que não está tão ruim!

Existe uma razão para nem sempre usar Handler.postDelayed?

alguma solução rápida para esse problema? Acabei de começar a usar RN com Firebase.

Tentei algumas soluções de temporizador diferentes, mas não tive sorte em fazer com que um funcionasse em segundo plano (de forma consistente).

Estou usando um setInterval para fazer ping em meu servidor a cada 10 minutos - acabei de notar esta mensagem de erro porque faço um setInterval(myFunction, 60000) . É algo com que preciso me preocupar?

Meu comportamento esperado é que, enquanto o aplicativo está aberto, ele pode enviar as solicitações, mas depois que o aplicativo é suspenso (pressionamento único do botão home) as solicitações devem parar de disparar. É esse o comportamento que posso esperar? A vida útil da bateria será afetada enquanto o aplicativo estiver suspenso?

Não há nada com que se preocupar aqui, e o aviso está incorreto e deve ser removido. Não há um "Módulo de temporização" para mantê-lo ativo, há apenas um retorno de chamada do Choreographer que verifica os temporizadores em cada quadro (o que também torna a granularidade mínima para os temporizadores de aproximadamente 16 ms).

Há um problema de correção no sentido de que os temporizadores não disparam em segundo plano no iOS ou Android. No entanto, este aviso parece ser especificamente sobre o Android e não parece ser preciso. Todos os temporizadores devem ser acionados quando o aplicativo voltar ao primeiro plano.

Algum plano de resolver isso em breve? O registro é irritante sempre que estamos testando em telefones Android.

Meu problema…

Eu estava recebendo este erro porque meu tempo limite foi de aproximadamente 59 minutos. O código para chamar um método no início da hora e tinha um setTimeout para criar um setInterval no próximo início da hora.

Minha solução ...

  1. atualize setTimeout para começar setInterval no início do minuto.
  2. atualize a função chamada pelo setInterval para executar a função apenas se os minutos atuais forem 0 .

Esta função agora funciona a cada minuto em vez de a cada hora ... mas como ela não faz nada 59 em 60 vezes, parece inofensiva.

Ei @beausmith , essa é realmente uma solução para simplesmente definir 60 timers? No meu entendimento, ele ainda está mantendo o timerModule ativo pela mesma hora, mas não está mostrando o aviso :)

@newah - sim, é um pouco maluco ... você pode sugerir uma solução melhor?

@beausmith , tentei usar https://github.com/ocetnik/react-native-background-timer, que usa timers nativos, o que parece uma solução - simplesmente substituí setTimout e setInterval com a implementação desta lib.

O suporte a temporizadores em segundo plano pode ser positivo ou negativo, dependendo de suas necessidades :))

Parece funcionar bem, mas não resolveu meus problemas :)

Eu recebo o mesmo aviso se usar o react-intl FormattedRelative. Porque dentro do componente FormattedRelative é usado o 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);
  }

Aqui está um exemplo de uso em meu código:

<FormattedRelative value={date}>
    {(message: string) => <Text>{message}</Text>}
</FormattedRelative>

Alguém sabe como consertar esse aviso?

Isso ainda é um problema. É particularmente irritante ao usar serviços do Firebase no Android. Por favor conserte.

Este é um bug muito ruim para serviços Firebase e quando você depende de aplicativos de terceiros. Porque o firebase não funciona se você alternar entre os aplicativos para verificar algo para continuar o processo.

Um exemplo é que usamos um sistema de login de terceiros / fator e o firebase para mover tokens. Mas, como limpamos, o token desaparece antes que a pessoa volte ao nosso aplicativo. Posso mudar o mecanismo de limpeza ou vocês podem consertar esse bug.

@vongohren, não que eu entenda seu caso de uso totalmente, mas uma estratégia alternativa pode ser armazenar a data de segundo plano em seu aplicativo na entrada para o segundo plano e, ao retornar ao primeiro plano, comparar a data atual com a data de segundo plano armazenada e realizar uma limpeza se um certo período de tempo tiver decorrido ...

Para elaborar meu caso de uso. Digamos que você precise dizer sim em outro aplicativo para aceitar o login. Feks muda para o aplicativo do Facebook para dizer sim a uma solicitação de login de um terceiro. O exemplo concreto com que estou trabalhando é o uPort, https://www.uport.me/. Então, quando contamos com uma maneira de enviar uma mensagem para nosso aplicativo, agora em segundo plano para dizer que está tudo bem, isso não funciona com o firebase, a menos que tomemos sua abordagem para limpar em um ponto posterior.

Acho que fiquei preso no fato de que a limpeza poderia acontecer logo após a informação emitida, porque então a informação já foi emitida, e não precisaríamos dela. Mas por que a abordagem firebase não funciona no react native? Que ele pare seus trabalhadores de fundo.

Preciso de um tempo limite de 30 minutos no meu aplicativo. Cada vez que o usuário interagir com a tela, ela será redefinida (jogue fora o tempo limite antigo, crie um novo).

Alguma alternativa de como fazer isso da maneira certa? É melhor lidar com isso com código nativo e passar algo para React quando terminar?

uau, então isso ainda é um problema

@nbennink verifique a resposta aqui . Atualmente não há uma maneira garantida de executar a tarefa em segundo plano, uma vez que existem vários fatores em jogo (economia de bateria, interação do usuário, etc.).

Eu também estou recebendo o mesmo aviso após usar o socket.io.
O aviso está abaixo:

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)

Os detalhes do meu pacote estão abaixo:

  • "react": "^ 16.3.0-alpha.1",
  • "reagir nativo": "0,54,4",
  • "socket.io": "^ 2.1.1",
  • "socket.io-client": "^ 2.1.1",

Alguma solução permanente descobriu?

Eu também estou recebendo o mesmo aviso após usar o socket.io.
O aviso está abaixo:

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)

Os detalhes do meu pacote estão abaixo:

"react": "^ 16.3.0-alpha.1",
"reagir nativo": "0,54,4",
"socket.io": "^ 2.1.1",
"socket.io-client": "^ 2.1.1",

Alguma solução permanente descobriu?

Idem, não há como contornar isso?

Ainda recebendo este aviso no react-native 0,57, este problema deveria estar aberto?

Quase um ano e React Native ainda tem o mesmo problema !!!

Enfrentando o mesmo problema, o mesmo aviso aparece 3-4 vezes. Muito irritante.

Existe alguma solução para resolver este problema?

Eliminou a mensagem de aviso por esta solução https://stackoverflow.com/a/48778011/7427111

É basicamente apenas esconder a mensagem e não resolver o problema, eu acho.

@dhavaljardosh Sim, seu palpite está certo.

Criei um cronômetro nativo com Java e Objective C. Funciona para mim e esconde a mensagem. Não tenho certeza se isso realmente melhora o desempenho. Como mencionado acima: ainda é ruim para a bateria, etc. porque há um longo temporizador no fundo.

Uma pergunta a fazer é se você realmente precisa de uma função de votação. Achei que sim, mas devido a esse problema, percebi que só precisava executar em _algo_ período semirrequente de tempo. Agora estou executando-o no plano de fundo do aplicativo e evitando totalmente esse problema. YMMV.

mesmo problema ao usar o Pusher ...

Vou testar meu aplicativo para ver qual é o impacto no desempenho ao definir um cronômetro a cada 60 segundos.
Presumo, no entanto, que alguns de vocês têm experiência com isso. Você pode compartilhar essa experiência, por favor? O impacto é significativo?

ainda o mesmo problema com a versão mais recente.
qualquer solução?

Também estou enfrentando esse problema.
O aplicativo estava funcionando bem, foi apenas quando comecei a usar o firebase que veio o aviso.
Qualquer solução?

Quero entender o significado por trás desta mensagem, pois começamos a recebê-la depois de um recurso recente que adicionamos ao nosso produto. O problema é _somente_ com o fato de que o cronômetro continua funcionando enquanto o aplicativo está em segundo plano? Seria uma solução adequada para se inscrever em eventos de estado do aplicativo e parar o cronômetro se o aplicativo estiver em segundo plano? Ou um cronômetro de longa duração com o aplicativo em _ primeiro plano_ também é problemático?

Quero entender o significado por trás desta mensagem, pois começamos a recebê-la depois de um recurso recente que adicionamos ao nosso produto. O problema é _somente_ com o fato de que o cronômetro continua funcionando enquanto o aplicativo está em segundo plano? Seria uma solução adequada para se inscrever em eventos de estado do aplicativo e parar o cronômetro se o aplicativo estiver em segundo plano? Ou um cronômetro de longa duração com o aplicativo em _ primeiro plano_ também é problemático?

Também gostaria de saber isso.

Além disso, o que acontece com setInterval? Não recebo avisos deste, mas tem o mesmo problema?

Quero entender o significado por trás desta mensagem, pois começamos a recebê-la depois de um recurso recente que adicionamos ao nosso produto. O problema é _somente_ com o fato de que o cronômetro continua funcionando enquanto o aplicativo está em segundo plano? Seria uma solução adequada para se inscrever em eventos de estado do aplicativo e parar o cronômetro se o aplicativo estiver em segundo plano? Ou um cronômetro de longa duração com o aplicativo em _ primeiro plano_ também é problemático?

A mensagem amarela diz "... e os temporizadores só podem ser chamados quando o aplicativo está em primeiro plano."
O módulo do cronômetro permanece ativo, mas os cronômetros não funcionam quando o aplicativo está em segundo plano? Então, o problema é manter o módulo do cronômetro ativo quando um aplicativo está em segundo plano? Quanto de desempenho / efeito de bateria isso teria, eu me pergunto ...

@BerndWessels https://www.npmjs.com/package/react-native-firebase Você já olhou isso? Diz que permite que o firebase seja executado no thread nativo. Não tenho certeza se isso é verdade, mas vale a pena dar uma olhada. Você pode até estar usando isso, não tenho certeza.

Você acha que se eu mudar para esta biblioteca, terei que mudar meu código existente? Quer dizer, isso não seria muito bom ...

mesmo problema com o Pusher no último RN0.59, e a interface do usuário pode travar por alguns segundos ...

Parece que só encontro esse problema quando faço uma chamada para o banco de dados em tempo real do firebase.

await firebase.database().ref('users/' + id).set(data);

RN 0.57.1

Não confirmei com 0,59.

@swushi Estou tendo o mesmo problema com RN 0.59

Estou recebendo o seguinte aviso ao usar a biblioteca 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 111862ms)

Como se livrar deste aviso ...

Alguém sabe como se livrar desse aviso sem escondê-lo ??
Eu uso a biblioteca firebase , não o react-native-firebase, mas parece ter o mesmo problema e não sei o quanto isso vai me afetar quando chegar à produção.

Estou muito preocupada se alguem puder ajudar por favor ...

Estou usando o seguinte código para contornar esse problema (no momento):

consulte https://github.com/firebase/firebase-js-sdk/issues/97#issuecomment -427512040

fixtimerbug.js

/////////////////////////////////////////////////////////////////////////////
////// temporary fix to bug about 'Setting a timer' /////////////////////////
////// See: https://github.com/pusher/pusher-js/issues/248 //////////////////
////// See: https://github.com/facebook/react-native/issues/12981 ///////////
////// See: https://github.com/firebase/firebase-js-sdk/issues/97 ///////////
/////////////////////////////////////////////////////////////////////////////
import { Platform, InteractionManager } from 'react-native';
const _setTimeout = global.setTimeout;
const _clearTimeout = global.clearTimeout;
const MAX_TIMER_DURATION_MS = 60 * 1000;
if (Platform.OS === 'android') {
  const timerFix = {};
  const runTask = (id, fn, ttl, args) => {
    const waitingTime = ttl - Date.now();
    if (waitingTime <= 1) {
      InteractionManager.runAfterInteractions(() => {
        if (!timerFix[id]) {
          return;
        }
        delete timerFix[id];
        fn(...args);
      });
      return;
    }
    const afterTime = Math.min(waitingTime, MAX_TIMER_DURATION_MS);
    timerFix[id] = _setTimeout(() => runTask(id, fn, ttl, args), afterTime);
  };
  global.setTimeout = (fn, time, ...args) => {
    if (MAX_TIMER_DURATION_MS < time) {
      const ttl = Date.now() + time;
      const id = '_lt_' + Object.keys(timerFix).length;
      runTask(id, fn, ttl, args);
      return id;
    }
    return _setTimeout(fn, time, ...args);
  };
  global.clearTimeout = id => {
    if (typeof id === 'string' && id.startsWith('_lt_')) {
      _clearTimeout(timerFix[id]);
      delete timerFix[id];
      return;
    }
    _clearTimeout(id);
  };
}
/////////////////////////////////////////////////////////////////////////////

@cpmech
Você implementa isso em um arquivo .js independente ou dentro do script que faz a exceção do temporizador

Olá, sim, o código acima está em um arquivo chamado 'fixtimerbug.js'. Então, eu o carrego em meu App.js assim:

import './src/fixtimerbug'; // <<<<<<<<<<<<<<<<<<

import React from 'react';
import { Platform, View, StatusBar } from 'react-native';
import AppNavigator from './src/navigation/AppNavigator';
import Store from './src/model/Store';

const store = new Store();

const App = () => (
  <View style={{ flex: 1, backgroundColor: '#fff' }}>
    {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
    <AppNavigator screenProps={{ store }} />
  </View>
);

export default App;

Parece que está funcionando bem, o que ele faz exatamente?

Muito obrigado @cpmech

Olá, o código (daqui: https://github.com/firebase/firebase-js-sdk/issues/97#issuecomment-427512040) simplesmente envolve a função setTimeout (global) para evitar que seja usado com um longo período.

Em nossa função global.setTimeout modificada, se o tempo de duração for maior que o limite MAX_TIMER_DURATION_MS , salvamos um id em um mapa local ( timerFix ) e então chame runTask que divide o tempo de duração em valores menores. runTask define um tempo limite (usando o _setTimeout ) com os pedaços menores que serão executados continuamente até que waitingTime seja muito pequeno. Quando waitingTime é pequeno o suficiente, chamamos de runAfterInteractions React Native para uma execução suave e, ao mesmo tempo, removemos a entrada id de nosso mapa local porque ganhamos não ligue para _setTimeout novamente neste caso. Observe que afterTime é alimentado em _setTimeout para evitar que seja chamado por um longo período.

Caso contrário, em nossa função global.setTimeout modificada, se o tempo de duração for menor que o limite MAX_TIMER_DURATION_MS , chamamos diretamente o setTimeout global (salvo em _setTimeout ).

O clearTimeout global também é empacotado para realizar um processo de limpeza removendo o id do mapa local.

Espero que ajude!

Enfrentando o mesmo problema ... Parece que teríamos que esconder o aviso por enquanto. Esta é a maneira mais curta de fazer isso:
componentDidMount() { console.disableYellowBox = true; ... }

Olá, o código (daqui: firebase / firebase-js-sdk # 97 (comentário) ) simplesmente envolve a função setTimeout (global) para evitar que seja usado por um longo período.

Em nossa função global.setTimeout modificada, se o tempo de duração for maior que o limite MAX_TIMER_DURATION_MS , salvamos um id em um mapa local ( timerFix ) e então chame runTask que divide o tempo de duração em valores menores. runTask define um tempo limite (usando o _setTimeout ) com os pedaços menores que serão executados continuamente até que waitingTime seja muito pequeno. Quando waitingTime é pequeno o suficiente, chamamos de runAfterInteractions React Native para uma execução suave e, ao mesmo tempo, removemos a entrada id de nosso mapa local porque ganhamos não ligue para _setTimeout novamente neste caso. Observe que afterTime é alimentado em _setTimeout para evitar que seja chamado por um longo período.

Caso contrário, em nossa função global.setTimeout modificada, se o tempo de duração for menor que o limite MAX_TIMER_DURATION_MS , chamamos diretamente o setTimeout global (salvo em _setTimeout ).

O clearTimeout global também é empacotado para realizar um processo de limpeza removendo o id do mapa local.

Espero que ajude!

Muito obrigado! Funcionou como um encanto.

Olá, o código (daqui: firebase / firebase-js-sdk # 97 (comentário) ) simplesmente envolve a função setTimeout (global) para evitar que seja usado por um longo período.

Em nossa função global.setTimeout modificada, se o tempo de duração for maior que o limite MAX_TIMER_DURATION_MS , salvamos um id em um mapa local ( timerFix ) e então chame runTask que divide o tempo de duração em valores menores. runTask define um tempo limite (usando o _setTimeout ) com os pedaços menores que serão executados continuamente até que waitingTime seja muito pequeno. Quando waitingTime é pequeno o suficiente, chamamos de runAfterInteractions React Native para uma execução suave e, ao mesmo tempo, removemos a entrada id de nosso mapa local porque ganhamos não ligue para _setTimeout novamente neste caso. Observe que afterTime é alimentado em _setTimeout para evitar que seja chamado por um longo período.

Caso contrário, em nossa função global.setTimeout modificada, se o tempo de duração for menor que o limite MAX_TIMER_DURATION_MS , chamamos diretamente o setTimeout global (salvo em _setTimeout ).

O clearTimeout global também é empacotado para realizar um processo de limpeza removendo o id do mapa local.

Espero que ajude!

Isso realmente resolve o problema ou apenas remove o aviso?

Olá, o código (daqui: firebase / firebase-js-sdk # 97 (comentário) ) simplesmente envolve a função setTimeout (global) para evitar que seja usado por um longo período.

Em nossa função global.setTimeout modificada, se o tempo de duração for maior que o limite MAX_TIMER_DURATION_MS , salvamos um id em um mapa local ( timerFix ) e então chame runTask que divide o tempo de duração em valores menores. runTask define um tempo limite (usando o _setTimeout ) com os pedaços menores que serão executados continuamente até que waitingTime seja muito pequeno. Quando waitingTime é pequeno o suficiente, chamamos de runAfterInteractions React Native para uma execução suave e, ao mesmo tempo, removemos a entrada id de nosso mapa local porque ganhamos não ligue para _setTimeout novamente neste caso. Observe que afterTime é alimentado em _setTimeout para evitar que seja chamado por um longo período.

Caso contrário, em nossa função global.setTimeout modificada, se o tempo de duração for menor que o limite MAX_TIMER_DURATION_MS , chamamos diretamente o setTimeout global (salvo em _setTimeout ).

O clearTimeout global também é empacotado para realizar um processo de limpeza removendo o id do mapa local.

Espero que ajude!

Muito obrigado! Isso não afetará o desempenho geral do aplicativo, certo?

Olá, o código (daqui: firebase / firebase-js-sdk # 97 (comentário) ) simplesmente envolve a função setTimeout (global) para evitar que seja usado por um longo período.
Em nossa função global.setTimeout modificada, se o tempo de duração for maior que o limite MAX_TIMER_DURATION_MS , salvamos um id em um mapa local ( timerFix ) e então chame runTask que divide o tempo de duração em valores menores. runTask define um tempo limite (usando o _setTimeout ) com os pedaços menores que serão executados continuamente até que waitingTime seja muito pequeno. Quando waitingTime é pequeno o suficiente, chamamos de runAfterInteractions React Native para uma execução suave e, ao mesmo tempo, removemos a entrada id de nosso mapa local porque ganhamos não ligue para _setTimeout novamente neste caso. Observe que afterTime é alimentado em _setTimeout para evitar que seja chamado por um longo período.
Caso contrário, em nossa função global.setTimeout modificada, se o tempo de duração for menor que o limite MAX_TIMER_DURATION_MS , chamamos diretamente o setTimeout global (salvo em _setTimeout ).
O clearTimeout global também é empacotado para realizar um processo de limpeza removendo o id do mapa local.
Espero que ajude!

Isso realmente resolve o problema ou apenas remove o aviso?

Não, não aborda o problema. É apenas uma boa solução.

Olá, o código (daqui: firebase / firebase-js-sdk # 97 (comentário) ) simplesmente envolve a função setTimeout (global) para evitar que seja usado por um longo período.
Em nossa função global.setTimeout modificada, se o tempo de duração for maior que o limite MAX_TIMER_DURATION_MS , salvamos um id em um mapa local ( timerFix ) e então chame runTask que divide o tempo de duração em valores menores. runTask define um tempo limite (usando o _setTimeout ) com os pedaços menores que serão executados continuamente até que waitingTime seja muito pequeno. Quando waitingTime é pequeno o suficiente, chamamos de runAfterInteractions React Native para uma execução suave e, ao mesmo tempo, removemos a entrada id de nosso mapa local porque ganhamos não ligue para _setTimeout novamente neste caso. Observe que afterTime é alimentado em _setTimeout para evitar que seja chamado por um longo período.
Caso contrário, em nossa função global.setTimeout modificada, se o tempo de duração for menor que o limite MAX_TIMER_DURATION_MS , chamamos diretamente o setTimeout global (salvo em _setTimeout ).
O clearTimeout global também é empacotado para realizar um processo de limpeza removendo o id do mapa local.
Espero que ajude!

Muito obrigado! Isso não afetará o desempenho geral do aplicativo, certo?

Não, não afeta o desempenho de forma alguma.

Uma nota rápida sobre

Se incomoda você apenas adiciona console.ignoredYellowBox = ['Configurando um cronômetro'];

e

Enfrentando o mesmo problema ... Parece que teríamos que esconder o aviso por enquanto. Esta é a maneira mais curta de fazer isso:
componentDidMount () {console.disableYellowBox = true; ...}

Esta não é uma solução viável. Os erros ainda acontecem, você simplesmente não recebe uma notificação.

Resultado? A análise de erro ainda precisa acontecer.

No caso de usar ouvintes do Firebase, por exemplo, você ocultará um grande número de erros, mas seu ambiente de desenvolvimento ainda os tratará.

Caso em questão: meu projeto baseado em expo continuava travando porque estava tentando mapear o código-fonte de cada erro do cronômetro.

Estou fechando isso porque não acho que o React Native adicionará suporte para AlarmManager no núcleo. Isso pode ser feito com módulos de terceiros ou código nativo personalizado.

A solução da @cpmech já funciona? Eu tentei em RN60.4, mas ainda recebo aviso de longo prazo.

Olá, acho que encontrei a solução:
Em primeiro lugar, você deve encontrar o seguinte arquivo em seu projeto: libraries / Core / Timers / JSTimer; js
Abra-o e você só tem que mudar este const MAX_TIMER_DURATION_MS, para aumentar acima de sua duração, escreveu no final do aviso!

aumentar o cronômetro, mas quanto?

@dulmandakh, você conhece algum projeto de código aberto que resolva esse problema?

@dulmandakh, você conhece algum projeto de código aberto que resolva esse problema?

RNFirebase talvez? @dulmandakh

Você pode reagendar o cronômetro com um tempo fixo até que expire:


const setLogoutTimer = expirationTime => {

  return dispatch => {
    timer = setTimeout(() => {
        if (expirationTime>60000)
        {
            console.log(`set new exp:${expirationTime-60000}`);
            dispatch(setLogoutTimer(expirationTime-60000));
        }
        else
        {
            console.log('logout');
            dispatch(logout());
        }
    }, 60000); 
  };
};

Já se passaram 2 anos que o Reag-Native ou o Firebase corrigiram esse problema corretamente. :(

OK, depois de algumas horas de longa investigação, tudo o que quero saber é se de alguma forma causei o problema.
Não tenho certeza de quando apareceu.
ele aparece assim que eu ligo para a firestore?
Não estou usando nenhum cronômetro no meu aplicativo

@luismasg Eu não uso setTimeout no meu código, pois é uma prática ruim, mas eu sei a origem do erro e não consigo pará-lo e parece que ele veio do estabelecimento de uma conexão remota com meu banco de dados, aparentemente a biblioteca que estou usando define o tempo limite para manter o TTL ativo, verifique sua biblioteca de dependências e veja se consegue identificar quem está causando o problema

Honestamente, eu não acho que usar temporizadores / intervalos JS seja uma boa ideia para temporizadores longos de qualquer maneira, o aviso existe por um motivo.

Use eventos de ciclo de vida para adicionar / remover temporizadores curtos. Se você precisar de uma execução real em segundo plano, use o código nativo com serviços de primeiro plano (Android) e tarefas em segundo plano (iOS).

Honestamente, eu não acho que usar temporizadores / intervalos JS seja uma boa ideia para temporizadores longos de qualquer maneira, o aviso existe por um motivo.

Como mencionado amplamente acima, muitas pessoas lutam com bibliotecas que usam temporizadores mais longos. "Apenas não use o firebase" não é uma solução.

Obviamente, este é um problema frequentemente encontrado, lidar com ele melhoraria a experiência do desenvolvedor de react-native que parece ser o objetivo final ...

Eu não diria que "não use firebase" é a resposta, mais como "conserte o firebase para não usar longos temporizadores e use os blocos de construção nativos apropriados para a tarefa em que estão usando longos temporizadores".

Mas você está certo, seria ideal para o RN oferecer suporte a long timers de alguma forma, mas considerando todas as advertências que existem (como maior consumo de bateria, tempo de execução desnecessário em segundo plano, etc.), isso só fará os desenvolvedores darem um tiro no próprio pé. Se houver alguma coisa, deve ser uma primitiva de temporizador / intervalo separada.

Se houver alguma coisa, deve ser uma primitiva de temporizador / intervalo separada.

Meu entendimento do que é esse problema é a falta de um a primitivo.

use o cronômetro dentro do cronômetro, isso é tudo!

@dulmandakh, por que este problema foi encerrado? O problema persiste e a discussão está em andamento?

Eu adicionei isso ao App.js, e isso corrigiu (temporário até que haja uma correção oficial)

const _setTimeout = global.setTimeout;
const _clearTimeout = global.clearTimeout;
const MAX_TIMER_DURATION_MS = 60 * 1000;
if (Platform.OS === "android") {
  // Work around issue `Setting a timer for long time`
  // see: https://github.com/firebase/firebase-js-sdk/issues/97
  const timerFix = {};
  const runTask = (id, fn, ttl, args) => {
    const waitingTime = ttl - Date.now();
    if (waitingTime <= 1) {
      InteractionManager.runAfterInteractions(() => {
        if (!timerFix[id]) {
          return;
        }
        delete timerFix[id];
        fn(...args);
      });
      return;
    }

    const afterTime = Math.min(waitingTime, MAX_TIMER_DURATION_MS);
    timerFix[id] = _setTimeout(() => runTask(id, fn, ttl, args), afterTime);
  };

  global.setTimeout = (fn, time, ...args) => {
    if (MAX_TIMER_DURATION_MS < time) {
      const ttl = Date.now() + time;
      const id = "_lt_" + Object.keys(timerFix).length;
      runTask(id, fn, ttl, args);
      return id;
    }
    return _setTimeout(fn, time, ...args);
  };

  global.clearTimeout = id => {
    if (typeof id === "string" && id.startWith("_lt_")) {
      _clearTimeout(timerFix[id]);
      delete timerFix[id];
      return;
    }
    _clearTimeout(id);
  };
}

Esta é uma solução alternativa, não uma correção.

Eu adicionei isso ao App.js, e isso corrigiu (temporário até que haja uma correção oficial)

...
    if (typeof id === "string" && id.startWith("_lt_")) {
...

'startWith' deve ser 'startsWith' se você for usar esse código.

Estou enfrentando isso com o AWS Amplify Datastore. Em vez de uma correção adequada, estou desativando o aviso com

import { YellowBox } from 'react-native';
YellowBox.ignoreWarnings(['Setting a timer']);

Não está claro para mim se o problema subjacente é algo que o RN precisa consertar, o Amplify precisa consertar ou é algo que eu preciso consertar. Obviamente, não quero problemas de desempenho.

Se incomodar, basta adicionar console.ignoredYellowBox = ['Setting a timer'];

Onde devo adicionar isso, tentei no arquivo de tela e no arquivo de ação, ainda não está funcionando

Basta colocá-lo em algum lugar do seu arquivo App.js. Eu coloquei no meu construtor, por exemplo.
Não sei se esse é o melhor lugar, então se alguém souber de algo melhor, eu gostaria de ouvir!

Acho que quem está usando Firebase pode resolver esse problema configurando a persistência do app após (signIn / signUp)

* depois de criar um novo usuário, ele fará login automaticamente

https://firebase.google.com/docs/auth/web/auth-state-persistence

então ... após a autenticação de um usuário, uma variável de tempo é criada.

o padrão para o firebase é local . "firebase.auth.Auth.Persistence.LOCAL"

você pode mudar usando:
firebase.auth.Auth.Persistence.SESSION
firebase.auth.Auth.Persistence.NONE

ou usando um localStorage ...

Até que resolvam de forma definitiva ...

importar {YellowBox} de "react-native";
importar _ de "lodash";
YellowBox.ignoreWarnings (["Configurando um cronômetro"]);
const _console = _.clone (console);
console.warn = (mensagem) => {
if (message.indexOf ("Configurando um cronômetro") <= -1) {
_console.warn (mensagem);
}
};

Uma vez resolvido, o código de lixo é removido e voila!

Isso pode ser resolvido com RxJS.

Basicamente, divide seu 5200 cronômetro em 5 * 1000 + 200 cronômetros.

import { of, range } from 'rxjs';
import {
  concatMap,
  delay,
  filter,
  mapTo,
  switchMap,
  tap,
} from 'rxjs/operators';

// ~ setTimeout(..., 5200);

const source = of(5200).pipe(
  switchMap(duration => {
    const times = Math.floor(duration / 1000); // = 5
    const remainder = duration % 1000; // = 200

    return range(1, times).pipe(
      concatMap(i => of(i).pipe(delay(1000))),
      tap(console.log),
      filter(i => i === times),
      delay(remainder),
      tap(console.log),
      mapTo('Done !'),
    );
  }),
);

source.subscribe(console.log);

1 // After 1s
2 // After 2s
3 // After 3s
4 // After 4s
5 // After 5s
5 // After 5s + 200 ms
'Done !'

Acho que quem está usando Firebase pode resolver esse problema configurando a persistência do app após (signIn / signUp)

* depois de criar um novo usuário, ele fará login automaticamente

https://firebase.google.com/docs/auth/web/auth-state-persistence

então ... após a autenticação de um usuário, uma variável de tempo é criada.

o padrão para o firebase é local . "firebase.auth.Auth.Persistence.LOCAL"

você pode mudar usando:
firebase.auth.Auth.Persistence.SESSION
firebase.auth.Auth.Persistence.NONE

ou usando um localStorage ...

Ótimo! Que tal para o Firestore?

Acho que quem está usando Firebase pode resolver esse problema configurando a persistência do app após (signIn / signUp)
* depois de criar um novo usuário, ele fará login automaticamente
https://firebase.google.com/docs/auth/web/auth-state-persistence
então ... após a autenticação de um usuário, uma variável de tempo é criada.
o padrão para o firebase é local . "firebase.auth.Auth.Persistence.LOCAL"
você pode mudar usando:
firebase.auth.Auth.Persistence.SESSION
firebase.auth.Auth.Persistence.NONE
ou usando um localStorage ...

Ótimo! Que tal para o Firestore?

se você estiver usando o antigo Firebase SDK, migre para o Firestore Admin SDK.

a maioria dos problemas será resolvida

https://firebase.google.com/docs/reference/admin

Ainda assim, ninguém conseguiu resolver esse problema. # vergonha para vocês, desenvolvedores; p

Ainda assim, ninguém conseguiu resolver esse problema. # vergonha para vocês, desenvolvedores; p

Nunca envergonhe os contribuidores de código aberto. É uma pena que isso não tenha sido corrigido, mas o código está aberto, se você souber como resolver, tenho certeza que eles apreciariam um pull-request.

Dito isso, por que diabos esse problema está encerrado .

Porque deve ser corrigido nas bibliotecas ofensivas. E as bibliotecas ofensivas não veem nenhum problema na forma como foi implementado.

Então ninguém vai consertar porque os dois lados não acham que está quebrado.

Eles podem ser envergonhados _pouco_ por isso 😄

Pessoal, acho que isso não foi "resolvido" pela simples razão de que é uma ideia extremamente ruim usar temporizadores longos.

Se você deseja executar temporizadores em segundo plano / eventos no futuro, precisará de uma biblioteca nativa para fazer isso. Isso também é especificamente difícil em ambas as plataformas devido às restrições de execução em segundo plano.

Baseado em 's comentário @eightyfive fizemos uma gota de substituição para rxjs6 delay , timeout & interval funções.

Ainda resta uma dúvida, alguém pode explicar se o "módulo Timer" permanece travado ao usar intervalos menores? Basicamente, apenas fazendo com que o cronômetro pare quando o aplicativo não está em primeiro plano. Ou isso apenas esconde o problema?

import { switchMap, concatMap, delay as nativeDelay, mapTo, filter, switchMapTo, repeat } from 'rxjs/operators'
import { range, of, race, throwError, TimeoutError } from 'rxjs'

const TIMER_INTERVAL = 1000
const reactNativeTimer = (duration) => {
  const times = Math.floor(duration / TIMER_INTERVAL)
  const remainder = duration % TIMER_INTERVAL
  if (times < 1) {
    return of(true).pipe(nativeDelay(remainder))
  }
  return range(1, times).pipe(
    concatMap(i => of(i).pipe(nativeDelay(TIMER_INTERVAL))),
    filter(i => i === times),
    nativeDelay(remainder)
  )
}

/**
 * React Native compatible version of delay pipe
 * <strong i="11">@param</strong> {number} duration in ms
 */
export const delay = (duration) => {
  return (source) => {
    return source.pipe(
      switchMap(next =>
        reactNativeTimer(duration).pipe(mapTo(next))
      )
    )
  }
}

/**
 * React Native compatible version of timeout pipe
 * <strong i="12">@param</strong> {number} duration in ms
 */
export const timeout = (duration) => {
  return (source) => {
    const timeoutTimer = reactNativeTimer(duration).pipe(
      switchMapTo(throwError(new TimeoutError()))
    )
    return race(source, timeoutTimer)
  }
}

/**
 * React Native compatible version of interval
 * <strong i="13">@param</strong> {number} duration in ms
 */
export const interval = (duration) => {
  return reactNativeTimer(duration).pipe(
    repeat()
  )
}

O engraçado é que todos neste tópico discutindo uma solução alternativa para ignorar esse aviso.
Quando fui redirecionado para este "problema" do aviso, esperava encontrar a documentação adequada, explicando "como?" para lidar com isso e um pouco mais de raciocínio sobre "por quê?".

#millennials front-end

Este #millenial gostaria de salientar que:

  • Este erro não requer documentação, é autoexplicativo: você definiu um cronômetro longo.
  • Este tópico está cheio de pessoas silenciando o aviso para bibliotecas que não possuem (por exemplo, firebase)
  • Como a discussão (produtiva) neste tópico indica, este é um problema mais complexo para corrigir dentro de react-native

Dito isso, ressaltarei novamente que este problema não deve ser encerrado, ele está marcado known-issue e Help Wanted .

Respostas Millenial para Millenial :)

Este erro não requer documentação, é autoexplicativo: você definiu um cronômetro longo.

  • Não é um erro, é um aviso (não me pergunte sobre a diferença, caso contrário, teremos que iniciar uma conversa filosófica profunda :))
  • Já pretendo estar documentado devido à referência fornecida a este tíquete, onde preciso ler toneladas de comentários em vez de resumo executivo (smth como um problema conhecido como você já sugeriu):
    image
  • Eu não diria que é 'autoexplicativo'. Não foi fornecido contexto suficiente. Literalmente, existem duas frases em uma descrição.

Dito isso, ressaltarei novamente que esse problema não deve ser fechado, ele está marcado como problema conhecido e busca-se ajuda.

Concordo plenamente. Isso economizará tempo para muitas pessoas que vêm da referência de URL no aviso.

Para usuários firebase / firestore: Eu simplesmente voltei a usar os pontos de extremidade REST diretamente para chamadas firestore. Como eu só precisava de auth e firestore, isso resolveu meus problemas com bastante facilidade. Snippet aqui:

https://stackoverflow.com/a/62446792

Fiz isso dentro de App () da seguinte maneira:

importar {YellowBox} de 'react-native';
função padrão de exportação App () {
YellowBox.ignoreWarnings (['Configurando um cronômetro']);
...
...
..
}

Lembrar

O que eu fiz e está funcionando comigo, mas ainda não sei se é uma boa prática ou não

Navegado para o arquivo

node_modulesreact-native \ Libraries \ Core \ TimersJSTimers.js

existe uma função const MAX_TIMER_DURATION_MS = 60 * 1000 e eu aumentei o tempo para ser 60 * 100000 e parou de aparecer

Olá,
Este problema tem cerca de 3 anos e ainda temos este aviso;)
como @RWOverdijk disse, ignorar o aviso não é uma solução.
Já existe uma solução que perdi?

Obrigado <3

Olá a todos, estamos decidindo bloquear esse problema. Sei que esse problema é frustrante para muitos de vocês e quero dedicar um tempo para ajudar a entender por que estamos tomando essa decisão.

Como os temporizadores funcionam no Android no React Native

O React Native tem uma implementação personalizada de temporizadores JS como setTimeout . No Android, a maneira como funciona é que os timers JS são rastreados no lado nativo, e o lado nativo decide quando acionar esses timers. Isso permite que o React Native seja mais inteligente sobre quando os timers são acionados e, em particular, como eles se relacionam com o ciclo de vida e a renderização do aplicativo.

O que acontece se eu definir um cronômetro com um intervalo longo?

Este é o meu entendimento, lendo o código e testando-o um pouco:

  1. Se você definir um cronômetro longo e o aplicativo for mantido aberto enquanto o cronômetro estiver ativo, o cronômetro ainda deve funcionar.
  2. Se você definir um cronômetro longo e o telefone estiver em segundo plano antes que o cronômetro termine, o cronômetro não será ativado até a próxima vez que você abrir o aplicativo.
  3. Há uma exceção para tarefas JS sem cabeça, que podem manter os cronômetros funcionando mesmo se o aplicativo estiver em segundo plano.

Por que não deveríamos chamar cronômetros com intervalos longos?

Acho que isso é principalmente uma questão de correção e menos sobre desempenho. O problema é que você não pode confiar em temporizadores longos se o usuário fizer o plano de fundo de seu aplicativo.

Se você não se importar em ter seu cronômetro ativado mais tarde, quando o aplicativo for colocado em primeiro plano novamente, acho que ignorar o aviso YellowBox é uma boa ideia.

Por outro lado, se o cronômetro espera ser chamado durante a vida útil de uma sessão e não deve ser acionado novamente em primeiro plano, você precisará fazer algo para garantir que o cronômetro seja ignorado em primeiro plano.

Como faço para usar bibliotecas de terceiros, como Socket.io e Firebase, que definem tempos longos?

Não sei os detalhes dessas bibliotecas de terceiros. Em última análise, tudo se resume à questão acima. Se essas bibliotecas de terceiros tiverem problemas para lidar com temporizadores sendo resolvidos em primeiro plano, você precisará descobrir uma maneira de consertar isso. Alguns comentários acima propõem maneiras de fazer isso para o Firebase, por exemplo. No final das contas, porém, você pode se beneficiar melhor contando com uma biblioteca desenvolvida especificamente com o React Native em mente.

Por outro lado, se a biblioteca de terceiros não tiver problemas com temporizadores sendo resolvidos em primeiro plano, você poderá ignorar o erro.

Por que setTimeout passar por AlarmManager , conforme sugerido inicialmente?

AlarmManager ativará o aplicativo se ele estiver em segundo plano e não achamos que cronômetros como setTimeout devam ativar o aplicativo. A maioria desses cronômetros longos é definida para coisas como tempos limite, e eles são relevantes apenas enquanto o aplicativo está aberto. Despertar um aplicativo em segundo plano é caro e só deve ser feito se o desenvolvedor tiver essa intenção explicitamente.

Ao mesmo tempo, muitas pessoas provavelmente precisam de algo assim. Poderíamos desenvolver uma API para ele que não passasse por setTimeout . Mas, devido ao esforço do Lean Core no momento, estamos tentando evitar o aumento da superfície da API para React Native. Achamos que esta é uma tarefa melhor realizada por uma biblioteca de terceiros como react-native-background-timer .

Por que você não remove o aviso totalmente, então?

Queremos que os desenvolvedores estejam cientes do que está acontecendo. Se alguém instalar uma biblioteca que depende da correção de setTimeout , achamos que o desenvolvedor deve estar ciente de que não podemos garantir essa correção. Queremos deixar que os desenvolvedores individuais tomem a decisão de se esse aviso é importante em seu caso de uso específico.

Esta página foi útil?
0 / 5 - 0 avaliações