Estou recebendo relatórios de usuários do meu aplicativo no mercado, fornecendo a seguinte exceção:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
Aparentemente, tem algo a ver com um FragmentManager, que não uso. O stacktrace não mostra nenhuma das minhas próprias classes, portanto, não tenho idéia de onde essa exceção ocorre e como evitá-la.
Para o registro: Eu tenho um tabhost e em cada guia existe um ActivityGroup alternando entre Atividades.
android
android-fragments
android-viewpager
illegalstateexception
fragmenttransaction
nhaarman
fonte
fonte
FragmentManager
, o Honeycomb certamente está. Isso está acontecendo em tablets Honeycomb reais? Ou será que alguém está executando um Honeycomb hackeado em um telefone ou algo assim e é essa edição hackeada que está tendo dificuldades?Respostas:
Por favor, verifique minha resposta aqui . Basicamente, eu apenas tinha que:
Não faça a chamada
super()
nosaveInstanceState
método. Isso estava atrapalhando as coisas ...Este é um bug conhecido no pacote de suporte.
Se você precisar salvar a instância e adicionar algo ao seu,
outState
Bundle
poderá usar o seguinte:No final, a solução adequada foi (como visto nos comentários) usar:
ao adicionar ou executar o
FragmentTransaction
que estava causando oException
.fonte
popBackStackImmediate
ela falhará imediatamente se o estado tiver sido salvo. A adição anterior do fragmento comcommitAllowingStateLoss
não desempenha nenhum papel. Meus testes mostram que isso é verdade. Não tem efeito sobre essa exceção específica. O que precisamos é de umpopBackStackImmediateAllowingStateLoss
método.Existem muitos problemas relacionados com uma mensagem de erro semelhante. Verifique a segunda linha desse rastreamento de pilha específico. Esta exceção está especificamente relacionada à chamada para
FragmentManagerImpl.popBackStackImmediate
.Essa chamada de método, como
popBackStack
, sempre falharáIllegalStateException
se o estado da sessão já tiver sido salvo. Verifique a fonte. Não há nada que você possa fazer para impedir que essa exceção seja lançada.super.onSaveInstanceState
não ajudará.commitAllowingStateLoss
não ajudará.Aqui está como eu observei o problema:
onSaveInstanceState
é chamado.popBackStackImmediate
é tentada.IllegalStateException
é jogado.Aqui está o que eu fiz para resolvê-lo:
Como não é possível evitar
IllegalStateException
o retorno de chamada, pegue e ignore.Isso é suficiente para impedir o aplicativo de travar. Mas agora o usuário restaurará o aplicativo e verá que o botão que eles pensavam ter pressionado não havia sido pressionado (eles acham). O fragmento do formulário ainda está sendo exibido!
Para corrigir isso, quando o diálogo for criado, indique algum estado para indicar que o processo foi iniciado.
E salve esse estado no pacote.
Não se esqueça de carregá-lo novamente em
onViewCreated
Em seguida, ao retomar, reverta os fragmentos se o envio foi tentado anteriormente. Isso impede que o usuário volte ao que parece ser um formulário não enviado.
fonte
popBackStackImmediate
foi chamado pelo próprio Android?Verifique se a atividade
isFinishing()
antes de mostrar o fragmento e preste atençãocommitAllowingStateLoss()
.Exemplo:
fonte
DialogFragment
. Consulte stackoverflow.com/questions/15729138/… para outras boas soluções, stackoverflow.com/a/41813953/2914140 me ajudou.É outubro de 2017 e o Google cria a Biblioteca de suporte do Android com o novo componente chamado Ciclo de vida. Ele fornece uma nova idéia para o problema 'Não é possível executar esta ação após onSaveInstanceState'.
Em resumo:
Versão mais longa com explicação:
por que esse problema aparece?
É porque você está tentando usar a
FragmentManager
partir de sua atividade (o que sustentará seu fragmento, suponho?) Para confirmar uma transação para você. Geralmente, parece que você está tentando fazer alguma transação para um fragmento que está por vir, enquanto a atividade do host já chama osavedInstanceState
método (o usuário pode tocar no botão de início para que a atividade chameonStop()
, no meu caso, é o motivo)Normalmente, esse problema não deve ocorrer - sempre tentamos carregar fragmentos em atividade desde o início, como se o
onCreate()
método fosse o local perfeito para isso. Mas às vezes isso acontece , especialmente quando você não pode decidir qual fragmento será carregado para essa atividade ou está tentando carregar fragmento de umAsyncTask
bloco (ou qualquer coisa levará um tempo). O tempo antes da transação do fragmento realmente acontecer, mas após oonCreate()
método da atividade , o usuário pode fazer qualquer coisa. Se o usuário pressionar o botão home, que aciona oonSavedInstanceState()
método da atividade ,can not perform this action
ocorrerá uma falha.Se alguém quiser ver mais detalhes desta edição, sugiro que dêem uma olhada nesta postagem do blog . Parece profundamente dentro da camada de código-fonte e explica muito sobre isso. Além disso, explica por que você não deve usar o
commitAllowingStateLoss()
método para solucionar esse problema (confie em mim, ele não oferece nada de bom para o seu código)Como consertar isto?
Devo usar o
commitAllowingStateLoss()
método para carregar fragmento? Não, você não deveria ;Devo substituir o
onSaveInstanceState
método, ignorar osuper
método dentro dele? Não, você não deveria ;Devo usar a
isFinishing
atividade interna mágica , para verificar se a atividade do host está no momento certo para a transação do fragmento? Sim, isso parece o caminho certo a fazer.Veja o que o componente Lifecycle pode fazer.
Basicamente, o Google faz alguma implementação dentro da
AppCompatActivity
classe (e várias outras classes base que você deve usar em seu projeto), o que facilita a determinação do estado atual do ciclo de vida . Dê uma olhada no nosso problema: por que esse problema aconteceria? É porque fazemos algo no momento errado. Portanto, tentamos não fazê-lo, e esse problema desaparecerá.Eu codigo um pouco para o meu próprio projeto, aqui está o que eu faço usando
LifeCycle
. Eu codigo em Kotlin.Como mostro acima. Vou verificar o estado do ciclo de vida da atividade do host. Com o componente Lifecycle na biblioteca de suporte, isso pode ser mais específico. O código
lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)
significa que, se o estado atual for pelo menosonResume
, o mais tardar? O que garante que meu método não seja executado durante algum outro estado de vida (comoonStop
).Está tudo pronto?
Claro que não. O código que mostrei mostra uma nova maneira de impedir a falha do aplicativo. Mas se for para o estado de
onStop
, essa linha de código não fará as coisas e, portanto, não mostrará nada na tela. Quando os usuários retornam ao aplicativo, eles verão uma tela vazia, que é a atividade do host vazio, sem mostrar nenhum fragmento. É uma experiência ruim (sim, um pouco melhor que uma batida).Então, aqui eu gostaria que houvesse algo mais agradável: o aplicativo não falhará se chegar ao estado de vida posterior
onResume
, o método de transação é sensível ao estado de vida; além disso, a atividade tentará continuar a concluir a ação da transação de fragmento depois que o usuário voltar ao nosso aplicativo.Eu adiciono algo mais a este método:
Eu mantenho uma lista dentro desta
dispatcher
classe, para armazenar esses fragmentos não tem chance de concluir a ação da transação. E quando o usuário volta da tela inicial e descobre que ainda existe um fragmento aguardando o lançamento, ele irá para oresume()
método sob a@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
anotação. Agora acho que deveria estar funcionando como eu esperava.fonte
FragmentDispatcher
usa uma lista para armazenar fragmentos pendentes se houver apenas um fragmento restaurado?Aqui está uma solução diferente para esse problema.
Usando uma variável de membro particular, você pode definir os dados retornados como uma intenção que pode ser processada após super.onResume ();
Igual a:
fonte
super.onActivityResult()
.Solução curta e funcional:
Siga etapas simples
Passos
Etapa 1: Substitua o
onSaveInstanceState
estado no respectivo fragmento. E remova super método dele.Etapa 2: usar
fragmentTransaction.commitAllowingStateLoss( );
em vez de
fragmentTransaction.commit( );
operações de fragmento while.fonte
CUIDADO , o uso
transaction.commitAllowingStateLoss()
pode resultar em uma experiência ruim para o usuário. Para obter mais informações sobre o motivo dessa exceção, consulte esta postagem .fonte
Encontrei uma solução suja para esse tipo de problema. Se você ainda deseja mantê-lo
ActivityGroups
por qualquer motivo (eu tinha motivos de limitação de tempo), basta implementarno seu
Activity
e faça algumback
código lá. mesmo que não exista esse método em dispositivos mais antigos, esse método será chamado pelos mais novos.fonte
Não use commitAllowingStateLoss (), ele deve ser usado apenas nos casos em que não há problema em o estado da interface do usuário mudar inesperadamente no usuário.
https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss ()
Se a transação ocorrer no ChildFragmentManager de parentFragment, use parentFragment.isResume () fora para verificar.
fonte
Eu tive um problema semelhante, o cenário era o seguinte:
O método onCreate da atividade foi assim:
A exceção foi lançada porque, quando a configuração é alterada (o dispositivo gira), a atividade é criada, o fragmento principal é recuperado do histórico do gerenciador de fragmentos e, ao mesmo tempo, o fragmento já possui uma referência OLD à atividade destruída.
alterar a implementação para isso resolveu o problema:
você precisa definir seus ouvintes toda vez que a atividade é criada para evitar a situação em que os fragmentos têm referências a instâncias destruídas antigas da atividade.
fonte
Se você herdar de
FragmentActivity
, deverá chamar a superclasse emonActivityResult()
:Se você não fizer isso e tentar mostrar uma caixa de diálogo de fragmento nesse método, poderá obter OPs
IllegalStateException
. (Para ser sincero, não entendo bem por que a chamada super corrige o problema. JáonActivityResult()
foi chamada antesonResume()
, portanto ainda não deve ser permitido mostrar uma caixa de diálogo de fragmento.)fonte
Eu estava recebendo essa exceção quando pressionava o botão Voltar para cancelar o seletor de intenção na atividade do fragmento do mapa. Eu resolvi isso substituindo o código de onResume (onde eu estava inicializando o fragmento) para onstart () e o aplicativo está funcionando bem.
fonte
Eu acho que usar
transaction.commitAllowingStateLoss();
não é a melhor solução. Essa exceção será lançada quando a configuração da atividade for alterada e o fragmentoonSavedInstanceState()
for chamado e, posteriormente, o método de retorno de chamada assíncrona tentar confirmar o fragmento.A solução simples pode ser verificar se a atividade está mudando a configuração ou não
por exemplo, cheque
isChangingConfigurations()
ie
if(!isChangingConfigurations()) { //commit transaction. }
Confira este link também
fonte
Possivelmente, a solução mais fácil e simples que encontrei no meu caso foi evitar estourar o fragmento ofensivo da pilha em resposta ao resultado da atividade. Então, alterando esta chamada no meu
onActivityResult()
:para isso:
ajudou no meu caso.
fonte
Se você estiver executando alguma FragmentTransaction no onActivityResult, o que você pode fazer, poderá definir algum valor booleano no onActivityResult e, no onResume, você poderá executar o FragmentTransaction com base no valor booleano. Por favor, consulte o código abaixo.
fonte
Cortesia: Solução para IllegalStateException
Esse problema me incomodou por muito tempo, mas felizmente eu vim com uma solução concreta para ele. Uma explicação detalhada disso está aqui .
Usar commitAllowStateloss () pode impedir essa exceção, mas levaria a irregularidades da interface do usuário. Até agora, entendemos que IllegalStateException é encontrada quando tentamos confirmar um fragmento após a perda do estado da atividade - portanto, devemos atrasar a transação até que o estado seja restaurado. .Pode ser feito simplesmente assim
Declarar duas variáveis booleanas privadas
Agora, em onPostResume () e onPause, definimos e desmarcamos nossa variável booleana isTransactionSafe. A idéia é marcar a transação segura apenas quando a atividade estiver em primeiro plano, para que não haja chance de perda de status.
-O que fizemos até agora salvará o IllegalStateException, mas nossas transações serão perdidas se forem feitas após a atividade ser movida para segundo plano, como commitAllowStateloss (). Para ajudar, temos a variável booleana isTransactionPending
fonte
Transações de fragmentos não devem ser executadas depois
Activity.onStop()
! Verifique se você não possui retornos de chamada que possam executar a transação depoisonStop()
. É melhor corrigir o motivo, em vez de tentar contornar o problema com abordagens como.commitAllowingStateLoss()
fonte
A partir da biblioteca de suporte versão 24.0.0, é possível chamar o
FragmentTransaction.commitNow()
método que confirma esta transação de forma síncrona, em vez de chamarcommit()
seguido porexecutePendingTransactions()
. Como a documentação diz essa abordagem ainda melhor:fonte
Sempre que você estiver tentando carregar um fragmento em sua atividade, verifique se a atividade está em retomada e não em estado de pausa. No estado de pausa, você pode acabar perdendo a operação de confirmação concluída.
Você pode usar transaction.commitAllowingStateLoss () em vez de transaction.commit () para carregar fragmento
ou
Crie um booleano e verifique se a atividade não será pausada
enquanto carrega a verificação de fragmentos
fonte
Para contornar esse problema, podemos usar o Componente de Arquitetura de Navegação , que foi introduzido no Google I / O 2018. O Componente de Arquitetura de Navegação simplifica a implementação da navegação em um aplicativo Android.
fonte
Em relação à ótima resposta para @Anthonyeef, aqui está um código de exemplo em Java:
fonte
Se você tiver travado com o método popBackStack () ou popBackStackImmediate (), tente corrigir com:
Isso também funcionou para mim.
fonte
No meu caso, recebi esse erro em um método de substituição chamado onActivityResult. Depois de cavar, descobri que talvez eu precisasse chamar ' super ' antes.
Eu adicionei e funcionou
Talvez você só precise adicionar um 'super' em qualquer substituição que estiver fazendo antes do seu código.
fonte
Extensão Kotlin
Uso:
fonte
fragment: Fragment
? Sim, tentei essa variante, mas nesse caso um fragmento seria criado em todos os casos (mesmo quando fragmentManager == null, mas não encontrei essa situação). Atualizei a resposta e alterei nulo para marcaraddToBackStack()
.Essa falha ocorre devido a uma FragmentTransaction ser confirmada após o ciclo de vida de sua propriedade já ter sido executado em SaveInstanceState. Isso geralmente é causado pela confirmação de FragmentTransactions de um retorno de chamada assíncrono. Confira o recurso vinculado para obter mais detalhes.
Transação de fragmentos e perda de estado da atividade
http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
fonte
Adicione isso em sua atividade
fonte
Eu também experimentei esse problema e o problema ocorre toda vez que o contexto
FragmentActivity
é alterado (por exemplo, a orientação da tela é alterada etc.). Portanto, a melhor solução é atualizar o contexto a partir do seuFragmentActivity
.fonte
Acabei criando um fragmento base e fiz com que todos os fragmentos do meu aplicativo o estendessem
Então, quando tento mostrar um fragmento, uso em
showAllowingStateLoss
vez deshow
como isso:
Eu vim para esta solução a partir deste PR: https://github.com/googlesamples/easypermissions/pull/170/files
fonte
Outra solução possível, que não tenho certeza se ajuda em todos os casos (origem aqui ):
fonte
Eu sei que há uma resposta aceita por @Ovidiu Latcu, mas depois de um tempo, o erro ainda persiste.
Crashlytics ainda está me enviando essa mensagem de erro estranha.
No entanto, agora o erro ocorre apenas na versão 7+ (Nougat). Minha correção foi usar commitAllowingStateLoss () em vez de commit () no fragmentTransaction.
Esta publicação é útil para commitAllowingStateLoss () e nunca mais teve um problema de fragmento.
Para resumir, a resposta aceita aqui pode funcionar em versões anteriores ao Nougat para Android.
Isso pode poupar alguém algumas horas de pesquisa. codificações felizes. <3 aplausos
fonte