recebendo exceção "IllegalStateException: Não é possível executar esta ação após onSaveInstanceState"

355

Eu tenho um aplicativo Live Android e, do mercado, recebi o rastreamento de pilha a seguir e não tenho idéia do por que isso está acontecendo porque não está acontecendo no código do aplicativo, mas está sendo causado por algum ou outro evento do aplicativo (suposição)

Não estou usando fragmentos, ainda há uma referência do FragmentManager. Se alguém puder esclarecer alguns fatos ocultos para evitar esse tipo de problema:

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.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
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.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:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
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:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)  
dcool
fonte
Você já encontrou uma solução? Tendo o mesmo problema aqui: stackoverflow.com/questions/7575921/…
nhaarman
11
Eu tive o mesmo problema e encontrou uma solução simples que funciona para mim
Phlebas
2
@phlebas Não, você não fez. Suas preocupações são diálogos, e isso não acontece. A linha superior da correspondência de rastreamento de pilha não é suficiente. O resto é muito diferente. Digo isso porque acabei de analisar o seu problema e infelizmente não ajuda em nada.
themightyjon
Você usa um Thread ou AsynTask nessa atividade?
José Castro
21
Discuto esse erro no meu blog ... você deve lê-lo. :)
Alex Lockwood

Respostas:

455

Este é o bug mais estúpido que encontrei até agora. Eu tinha um Fragmentaplicativo funcionando perfeitamente para a API <11 e Force Closingna API> 11 .

Eu realmente não conseguia descobrir o que eles mudaram dentro do Activityciclo de vida na chamada para saveInstance, mas aqui está como resolvi isso:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

Eu simplesmente não faço a ligação .super()e tudo funciona muito bem. Espero que isso poupe algum tempo.

EDIT: após mais algumas pesquisas, esse é um bug conhecido no pacote de suporte.

Se você precisar salvar a instância e adicionar algo ao seu, outState Bundlepoderá usar o seguinte:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

EDIT2: isso também pode ocorrer se você estiver tentando executar uma transação depois que a sua Activityfor colocada em segundo plano. Para evitar isso, você deve usarcommitAllowingStateLoss()

EDIT3: As soluções acima estavam corrigindo problemas nas bibliotecas support.v4 anteriores, pelo que me lembro. Mas se você ainda tiver problemas com isso, DEVE também ler o blog de @AlexLockwood : Transações de fragmentos e perda de estado de atividade

Resumo da postagem do blog (mas eu recomendo fortemente que você a leia):

  • NUNCA commit() transações após onPause()pré-Honeycomb e onStop()pós-Honeycomb
  • Tenha cuidado ao confirmar transações dentro dos Activitymétodos do ciclo de vida. Use onCreate() , onResumeFragments()eonPostResume()
  • Evite realizar transações dentro de métodos de retorno de chamada assíncrono
  • Use commitAllowingStateLoss()apenas como último recurso
Ovidiu Latcu
fonte
97
Você deve usar commitAllowingStateLoss () em vez de commit ()
meh
7
Portanto, não chamar super no onSaveInstanceState impedirá o FragmentManager de salvar o estado de todos os fragmentos e restaurá-los. Isso pode causar problemas com a rotação. Além disso, eu tentei outra coisa sobre colocar lixo no pacote e isso não faz diferença para mim. Não tenho certeza como seria - o erro que você referenciado no pacote de apoio é uma NullPointerException, e não parece muito parecido com este IllegalStateException ...
themightyjon
56
@meh commitAllowingStateLoss()evita apenas a exceção. Não protege seu aplicativo contra perda acidental de estado. Veja esta postagem do blog .
Alex Lockwood
2
@AlexLockwood, a partir desse post do blog, podemos aprender que devemos fazer todas as nossas chamadas de rede dentro do fragmento (e exibir algum progresso temporário da interface do usuário, se necessário) e essa é a única maneira de evitar essa exceção quando ela provavelmente ocorre porque confirma está sendo chamado após alguma chamada de método assíncrona.
meh
11
Eu não entendo o primeiro ponto: "NUNCA confirme transações () depois de ... onStop () no pós-Honeycomb". E se eu precisar de um botão para acionar um fragmento para ser substituído por outro? Devo colocar um booleano que verifique se a atividade foi concluída no OnStop e, se o fizer, chame commitAllowingStateLoss? Além disso, e se eu tiver um fragmento dentro de um fragmento, que precise substituir ao clicar em um botão?
desenvolvedor android
76

A pesquisa no código-fonte do Android sobre o que causa esse problema indica que o sinalizador mStateSaved na FragmentManagerImplclasse (instância disponível em Activity) tem valor true. É definido como true quando a pilha de trás é salva (saveAllState) de plantão Activity#onSaveInstanceState. Posteriormente, as chamadas do ActivityThread não redefinem esse sinalizador usando os métodos de redefinição disponíveis de FragmentManagerImpl#noteStateNotSaved()e dispatch().

A meu ver, existem algumas correções disponíveis, dependendo do que seu aplicativo está fazendo e usando:

Boas maneiras

Antes de mais nada: eu anunciaria o artigo de Alex Lockwood . Então, pelo que eu fiz até agora:

  1. Para fragmentos e atividades que não precisam manter nenhuma informação de estado, chame commitAllowStateLoss . Retirado da documentação:

    Permite que a confirmação seja executada após o estado de uma atividade ser salvo. Isso é perigoso porque a confirmação pode ser perdida se a atividade precisar ser restaurada posteriormente de seu estado; portanto, isso só deve ser usado nos casos em que é aceitável que o estado da UI mude inesperadamente no usuário`. Eu acho que isso é bom de usar se o fragmento está mostrando informações somente leitura. Ou mesmo se eles mostrarem informações editáveis, use os métodos de retorno de chamada para manter as informações editadas.

  2. Logo após a transação ser confirmada (você acabou de commit()ligar), faça uma chamada para FragmentManager.executePendingTransactions().

Maneiras não recomendadas:

  1. Como Ovidiu Latcu mencionou acima, não ligue super.onSaveInstanceState(). Mas isso significa que você perderá todo o estado de sua atividade junto com o estado de fragmentos.

  2. Substituir onBackPressede apenas chamar lá finish(). Isso deve ser bom se o aplicativo não usar a API de fragmentos; como super.onBackPressedexiste uma chamada para FragmentManager#popBackStackImmediate().

  3. Se você estiver usando a API de fragmentos e o estado de sua atividade for importante / vital, tente chamar usando a API de reflexão FragmentManagerImpl#noteStateNotSaved(). Mas isso é um hack, ou pode-se dizer que é uma solução alternativa. Não gosto, mas no meu caso é bastante aceitável, pois tenho um código de um aplicativo herdado que usa código obsoleto ( TabActivitye implicitamente LocalActivityManager).

Abaixo está o código que usa reflexão:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    invokeFragmentManagerNoteStateNotSaved();
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
    /**
     * For post-Honeycomb devices
     */
    if (Build.VERSION.SDK_INT < 11) {
        return;
    }
    try {
        Class cls = getClass();
        do {
            cls = cls.getSuperclass();
        } while (!"Activity".equals(cls.getSimpleName()));
        Field fragmentMgrField = cls.getDeclaredField("mFragments");
        fragmentMgrField.setAccessible(true);

        Object fragmentMgr = fragmentMgrField.get(this);
        cls = fragmentMgr.getClass();

        Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
        noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
        Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
    } catch (Exception ex) {
        Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
    }
}

Felicidades!

gunar
fonte
Isso parece acontecer com o ActionBarSherlock também, no Gingerbread, portanto, o caso de verificar o Build Id parece discutível ... :(
t0mm13b
Além disso, você deve indicar - que não é adequado para uso ABS quer :)
t0mm13b
@ t0mm13b: O código acima representa o meu projeto, pois ele não usa nem fragmentos, nem support.FragmentActivity. É executado no android.app.Activity e, como a inconsistência que aciona a exceção é causada pelo FragmentManager (API nível 11 para cima), é por isso que a verificação ... Se você acredita que a fonte do mal é a mesma para você, sinta-se à vontade para remover a verificação. O ABS é uma história diferente, pois é executado sobre o pacote de compatibilidade e a implementação do support.FragmentActivity pode usar a mesma implementação do FragmentManager e voila: mesmo problema.
gunar
@ t0mm13b: para adicionar mais de 600 caracteres não basta, você deve investigar primeiro o que realmente está causando isso para você. Você também deve perceber que o código acima é um truque feio e não assumirei nenhuma responsabilidade se ele funcionar ou não (para mim foi a melhor solução, considerando as circunstâncias). Se você precisar usá-lo, verifique novamente o código-fonte compat para obter nomes de variáveis, pois esses podem diferir do pacote padrão. Espero que esta questão iria ser resolvido através próximas versões do pacote de compatibilidade, mas a partir da experiência Android, há poucas chances de acontecer ...
Gunar
Uhhh ... É exatamente o mesmo problema deste relatório de bug, que é o ponto da pergunta deste OP. Eu mantenho o meu comentário - você deveria ter explicitamente negado um aviso e dizer que não é garantido e também deve ter declarado que não usou fragmentos - caso contrário, por que se incomodar em postar essa resposta também? :) Só
estou
35

Essa exceção ocorrerá se você tentar executar uma transição de fragmento após a onSaveInstanceState()chamada de sua atividade de fragmento .

Uma razão pela qual isso pode acontecer é se você deixar um AsyncTask(ou Thread) em execução quando uma atividade for interrompida.

Qualquer transição após a onSaveInstanceState()chamada pode se perder se o sistema recuperar a atividade por recursos e recriá-la mais tarde.

FunkTheMonk
fonte
2
Hey Funk, tenho uma pergunta aqui, como é que o onBackPressed pode ser chamado nessa atividade ou fragmento, se essa atividade ou fragmento foi interrompido. A exceção acima parece ser gerada a partir de algum evento da interface do usuário (ou seja, pressionar a tecla VOLTAR), mas não consigo descobrir a relação entre a tarefa Async e a tecla Voltar.
dcool
Como você pode salvar as transições de fragmentos para o estado reverso, pressionar o botão Voltar pode causar o inverso da transição salva (para que fragmentos antigos retornem). onSaveInstanceState é chamado antes que sua atividade seja destruída para restaurar recursos no sistema, nem sempre depois que o onStop é chamado. Desculpe, isso não ficou muito claro na minha resposta.
FunkTheMonk
Funk, mas não estou usando nenhum fragmento no meu aplicativo. Pode ser os fragmentos usados ​​no código nativo. antes eu pensava que você estava falando o mesmo.
dcool
11
Eu tinha um AsyncTask com uma referência a um fragmento. Problema resolvido após remover a chamada super () de onSaveInstanceState e substituir a referência do meu AsyncTask por um WeakReference <Fragment>.
Buffalo
2
@ Buffalo Isso definitivamente não é uma solução para o problema. Você deve sempre ligar super.onSaveInstanceState().
Alex Lockwood
27

Simplesmente chame super.onPostResume () antes de mostrar seu fragmento ou mova seu código no método onPostResume () depois de chamar super.onPostResume (). Isso resolve o problema!

MJ.Ahmadi
fonte
6
Chamar onPostResume () garante que onResumeFragments () seja chamado e, para mim, esta é a solução ideal.
J2emanue
20

Isso também pode acontecer ao chamar dismiss()um fragmento de caixa de diálogo após a tela ser bloqueada \ em branco e o estado da instância da caixa de diálogo Atividade + ter sido salvo. Para contornar esta chamada:

dismissAllowingStateLoss()

Literalmente, toda vez que estou descartando uma caixa de diálogo, não me importo mais com o seu estado, de modo que está tudo bem, pois você não está perdendo nenhum estado.

Brian Dilley
fonte
2
Este foi o meu problema exato! Você é brilhante!
Tash Pemhiwa
17

Solução curta e funcional:

Siga etapas simples:

Etapa 1 : Substitua o estado onSaveInstanceState no respectivo fragmento. E remova super método dele.

@Override
public void onSaveInstanceState(Bundle outState) {
};

Etapa 2 : Use CommitAllowingStateLoss (); em vez de commit (); enquanto operações de fragmento.

fragmentTransaction.commitAllowingStateLoss();
Basbous
fonte
11
Remover o super método fez o truque, você pode explicar por que? É seguro remover isso?
6776 Bruce Bruce
7
não é seguro remover super (), você perderá outros dados conf depois disso!
deadfish 28/02
7

isso funcionou para mim ... descobri isso sozinho ... espero que ajude!

1) NÃO possui um FragmentManager / FragmentTransaction global "estático".

2) onCreate, SEMPRE inicialize o FragmentManager novamente!

amostra abaixo: -

public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedInstanceState = savedInstanceState;
    setDefaultFragments();
}

protected void setDefaultFragments() {
    fragmentManager = getSupportFragmentManager();
    //check if on orientation change.. do not re-add fragments!
    if(mSavedInstanceState == null) {
        //instantiate the fragment manager

        fragmentTransaction = fragmentManager.beginTransaction();

        //the navigation fragments
        NavigationFragment navFrag = new NavigationFragment();
        ToolbarFragment toolFrag = new ToolbarFragment();

        fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
        fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
        fragmentTransaction.commitAllowingStateLoss();

        //add own fragment to the nav (abstract method)
        setOwnFragment();
    }
}
kurayami88
fonte
6

Eu estava sempre recebendo isso quando tentei mostrar fragmentos no método onActivityForResult (), então o problema era o seguinte:

  1. Minha atividade está pausada e interrompida, o que significa que onSaveInstanceState () já foi chamado (para dispositivos pré-Honeycomb e pós-Honeycomb).
  2. Em caso de algum resultado, eu fiz a transação para mostrar / ocultar o fragmento, o que causa essa IllegalStateException.

O que eu fiz é o seguinte:

  1. Valor agregado para determinar se a ação que eu quero foi executada (por exemplo, tirar uma foto de camere - isPhotoTaken) - pode ser um valor booleano ou inteiro, dependendo de quantas transações diferentes você precisar.
  2. No método onResumeFragments () substituído, verifiquei meu valor e, depois de fazer transações fragmentadas, precisei. Nesse caso, commit () não foi feito após onSaveInstanceState, pois o estado foi retornado no método onResumeFragments ().
Matriz
fonte
5

Resolvi o problema com onconfigurationchanged. O truque é que, de acordo com o ciclo de vida da atividade do Android, quando você chamava explicitamente uma intenção (intenção da câmera ou qualquer outra); a atividade é pausada e o onsavedInstance é chamado nesse caso. Ao girar o dispositivo para uma posição diferente daquela em que a atividade estava ativa; executar operações de fragmento, como confirmação de fragmento, causa exceção de estado ilegal. Há muitas reclamações sobre isso. É algo sobre gerenciamento do ciclo de vida da atividade do Android e chamadas de método adequadas. Para resolvê-lo, fiz o seguinte: 1-Substitua o método onsavedInstance da sua atividade e determine a orientação atual da tela (retrato ou paisagem) e defina a orientação da tela antes que sua atividade seja pausada. dessa forma, a atividade que você bloqueia a rotação da tela para a sua atividade, caso tenha sido girada por outra. 2 - substitua um método de atividade de retomada e defina seu modo de orientação agora para sensor, para que, depois que o método salvo seja chamado, ele chame mais uma vez na configuração para lidar com a rotação corretamente.

Você pode copiar / colar este código em sua atividade para lidar com ele:

@Override
protected void onSaveInstanceState(Bundle outState) {       
    super.onSaveInstanceState(outState);

    Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
    int orientation =this.getDisplayOrientation();
    //Lock the screen orientation to the current display orientation : Landscape or Potrait
    this.setRequestedOrientation(orientation);
}

//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device 
public int getDisplayOrientation() {
    Display getOrient = getWindowManager().getDefaultDisplay();

    int orientation = getOrient.getOrientation();

    // Sometimes you may get undefined orientation Value is 0
    // simple logic solves the problem compare the screen
    // X,Y Co-ordinates and determine the Orientation in such cases
    if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        Configuration config = getResources().getConfiguration();
        orientation = config.orientation;

        if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        // if height and widht of screen are equal then
        // it is square orientation
            if (getOrient.getWidth() == getOrient.getHeight()) {
                orientation = Configuration.ORIENTATION_SQUARE;
            } else { //if widht is less than height than it is portrait
                if (getOrient.getWidth() < getOrient.getHeight()) {
                    orientation = Configuration.ORIENTATION_PORTRAIT;
                } else { // if it is not any of the above it will defineitly be landscape
                    orientation = Configuration.ORIENTATION_LANDSCAPE;
                }
            }
        }
    }
    return orientation; // return value 1 is portrait and 2 is Landscape Mode
}

@Override
public void onResume() {
    super.onResume();
    Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} 
douggynix
fonte
4

Eu tive o mesmo problema, obtendo IllegalStateException, mas substituir todas as minhas chamadas para commit () por commitAllowingStateLoss () não ajudou.

O culpado foi uma ligação para DialogFragment.show ().

Eu o envolvo

try {
    dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
    return;
}

e fez isso. OK, não consigo mostrar a caixa de diálogo, mas neste caso estava tudo bem.

Era o único lugar no meu aplicativo em que chamei FragmentManager.beginTransaction (), mas nunca chamei commit (), então não o encontrei quando procurei "commit ()".

O engraçado é que o usuário nunca sai do aplicativo. Em vez disso, o assassino era um anúncio intersticial da AdMob sendo exibido.

Roger CS Wernersson
fonte
3
O mesmo aqui. Eu resolvi Substituindo o método 'show (gerente do FragmentManager, tag String)', substituindo 'commit' por 'commitAllowingStateLoss'; perdendo alguma coisa porque não consigo definir dois atributos particulares do Diálogo: mDismissed e mShownByMe. Mas parece que trabalhar cada vez :)
Francesco Ditrani
Eu fiz uma solução alternativa ao DialogFragment, que pode evitar essa exceção: github.com/AndroidDeveloperLB/DialogShard
desenvolvedor android
4

Minha solução para esse problema foi

No fragmento, adicione métodos:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
    guideMap = guideMapFragment.getMap();
    ...
}

@Override
public void onDestroyView() {
    SherlockFragmentActivity a = getSherlockActivity();
    if (a != null && guideMapFragment != null) {
        try {
            Log.i(LOGTAG, "Removing map fragment");
            a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
            guideMapFragment = null;
        } catch(IllegalStateException e) {
            Log.i(LOGTAG, "IllegalStateException on exit");
        }
    }
    super.onDestroyView();
}

Pode ser ruim, mas não foi possível encontrar nada melhor.

mc.dev
fonte
Verdades .. capturar a exceção pode evitar a falha do aplicativo, mas o comportamento emite esses fragmentos deixados na tela ou não foram adicionados.
Marcos Vasconcelos
4
continue rolando. a verdade está lá fora
anil
4

Eu consegui esse problema, mas acho que esse problema não está relacionado a commit e commitAllowStateLoss.

O seguinte rastreamento de pilha e mensagem de exceção é sobre commit ().

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

Mas essa exceção foi causada por onBackPressed ()

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)

Todos foram causados ​​por checkStateLoss ()

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }

mStateSaved será verdadeiro após onSaveInstanceState.

Esse problema raramente ocorre. Nunca encontrei esse problema. Não posso repetir o problema.

Encontrei o problema 25517

Pode ter ocorrido nas seguintes circunstâncias

  1. A tecla Voltar é chamada após onSaveInstanceState, mas antes que a nova atividade seja iniciada.

  2. use onStop () no código

Não sei ao certo qual é a raiz do problema. Então eu usei uma maneira feia.

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}
oO_ox
fonte
Realmente não resolvi o problema, mas esse problema não está relacionado a commit e commitAllowStateLoss.
oO_ox
4

Eu tenho o mesmo problema no meu aplicativo. Eu resolvi esse problema chamando a super.onBackPressed();classe anterior e commitAllowingStateLoss()a classe atual com esse fragmento.

Pedro
fonte
2
Obrigado. Esta solução resolvido problema uisng commitAllowingStateLoss()em vez decommit()
Chintak Patel
Evite usar commitAllowingStateLoss () medium.com/@elye.project/…
swooby
3

onSaveInstance será chamado se um usuário girar a tela para carregar os recursos associados à nova orientação.

É possível que esse usuário tenha girado a tela e pressionado o botão Voltar (porque também é possível que esse usuário atrapalhe o telefone enquanto estiver usando o aplicativo)

dols
fonte
2
Embora alterações na configuração (como alterações na orientação) possam resultar nessa exceção, elas não são a causa raiz.
quer
2

Mesmo problema para mim e após um dia de análise de todos os artigos, blog e stackoverflow, encontrei uma solução simples. Não use o método SavedInstanceState, esta é a condição com uma linha de código. No código do fragmento:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....
Tobia Caneschi
fonte
2

Isso acontece sempre que você está tentando carregar um fragmento, mas a atividade mudou de estado para onPause () .Por exemplo, quando você tenta buscar dados e carregá-los na atividade, mas quando o usuário clica em algum botão e pressiona mudou para a próxima atividade.

Você pode resolver isso de duas maneiras

Você pode usar transaction.commitAllowingStateLoss () em vez de transaction.commit () para carregar o fragmento, mas pode acabar perdendo a operação de confirmação concluída.

ou

Certifique-se de que a atividade esteja retomada e não entre no estado de pausa ao carregar um fragmento. Crie um booleano e verifique se a atividade não está no estado onPause ().

@Override
public void onResume() {
    super.onResume();
    mIsResumed = true;
}

@Override
public void onPause() {
    mIsResumed = false;
    super.onPause();
}

ao carregar o fragmento, verifique se há atividade presente e carregue apenas quando a atividade estiver em primeiro plano.

if(mIsResumed){
 //load the fragment
}
Sharath kumar
fonte
1

Obrigado @ gunar, mas acho que existe uma maneira melhor.

De acordo com o documento:

 * If you are committing a single transaction that does not modify the
 * fragment back stack, strongly consider using
 * {@link FragmentTransaction#commitNow()} instead. This can help avoid
 * unwanted side effects when other code in your app has pending committed
 * transactions that expect different timing.
 *
 * @return Returns true if there were any pending transactions to be
 * executed.
 */
public abstract boolean executePendingTransactions();

Então use commitNowpara substituir:

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()
JianxinLi
fonte
0

Bem, depois de tentar todas as soluções acima sem sucesso (porque basicamente eu não tenho transações).

No meu caso, eu estava usando AlertDialogs e ProgressDialog como fragmentos que, às vezes, na rotação, ao solicitar o FragmentManager, o erro aumenta.

Eu encontrei uma solução alternativa misturando alguns posts semelhantes:

É uma solução de 3 etapas, tudo feito em sua FragmentActivity (neste caso, é chamada GenericActivity):

private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    //To avoid bug for fragments: Step 2 of 3
    activity = new WeakReference<GenericActivity>(this);
}

@Override
public FragmentManager getSupportFragmentManager(){
    //To avoid bug for fragments: Step 3 of 3
    if (this == activity.get()) {
        return super.getSupportFragmentManager();
    }
    return activity.get().getSupportFragmentManager();
}
Antilope
fonte
0

Quando eu uso a atividade de inicialização em um fragmento, receberei essa exceção;

Quando mudo para usar startactivityforresult, a exceção desaparece :)

Portanto, a maneira mais fácil de corrigi-lo é usar a API startActivityForResult :)

Questão
fonte
0

Eu estava recebendo essa exceção ao pressionar o botão Voltar para cancelar o seletor de intenção na atividade de fragmentos do mapa. Resolvi isso substituindo o código de onResume () (onde estava inicializando o fragmento e confirmando a transação) por onStart () e o aplicativo está funcionando bem agora. Espero que ajude.

DCS
fonte
0

Isso foi corrigido no Android 4.2 e também na fonte da biblioteca de suporte. [*]

Para obter detalhes da causa (e soluções alternativas), consulte o relatório de erros do Google: http://code.google.com/p/android/issues/detail?id=19917

Se você estiver usando a biblioteca de suporte, não precisará se preocupar com esse bug (por muito tempo) [*]. No entanto, se você estiver usando a API diretamente (ou seja, não usando o FragmentManager da biblioteca de suporte) e segmentando uma API abaixo do Android 4.2, precisará tentar uma das soluções alternativas.

[*] No momento da redação, o Android SDK Manager ainda está distribuindo uma versão antiga que exibe esse bug.

Editar Vou acrescentar alguns esclarecimentos aqui, porque obviamente de alguma forma confundi quem recusou esta resposta.

Existem várias circunstâncias diferentes (mas relacionadas) que podem causar o lançamento dessa exceção . Minha resposta acima está se referindo à instância específica discutida na pergunta, ou seja, um bug no Android que foi corrigido posteriormente. Se você está recebendo essa exceção por outro motivo, é porque está adicionando / removendo fragmentos quando não deveria estar (depois que os estados dos fragmentos foram salvos). Se você estiver nessa situação, talvez " Fragmentos Aninhados - IllegalStateException" Não é possível executar esta ação após onSaveInstanceState " " possa ser útil para você.

Benjamin Dobell
fonte
0

Meu caso de uso: usei o ouvinte no fragmento para notificar a atividade que algo aconteceu. Eu fiz o novo fragmento confirmar no método de retorno de chamada. Isso funciona perfeitamente bem na primeira vez. Mas, na mudança de orientação, a atividade é recriada com o estado da instância salva. Nesse caso, o fragmento não é criado novamente implica que o fragmento tem o ouvinte que é uma atividade destruída antiga. De qualquer maneira, o método de retorno de chamada será acionado na ação. Vai para a atividade destruída que causa o problema. A solução é redefinir o ouvinte no fragmento com a atividade ativa atual. Isso resolve o problema.

Ganesh Kanna
fonte
0

O que descobri é que, se outro aplicativo for do tipo caixa de diálogo e permitir que toques sejam enviados para o aplicativo em segundo plano, quase qualquer aplicativo em segundo plano falhará com esse erro. Acho que precisamos verificar sempre que uma transação for executada se a instância foi salva ou restaurada.

chris
fonte
0

No meu caso, com a mesma exceção de erro, coloquei o "onBackPressed ()" em um executável (você pode usar qualquer uma das suas visualizações):

myView.post(new Runnable() {
                    @Override
                    public void run() {
                        onBackPressed()
                    }
                });

Eu não entendo o porquê, mas funciona!

Alecs
fonte
A postagem em uma exibição só executará o Runnable depois que a exibição tiver sido adequadamente projetada e desenhada na tela; Isso muitas vezes significa a atividade em si foi totalmente retomada, portanto, sem problemas
Mercato
0

Você pode estar chamando fragmentManager.popBackStackImmediate (); quando a atividade está em pausa. A atividade não está concluída, mas está em pausa e não está em primeiro plano. Você precisa verificar se a atividade está em pausa ou não antes de popBackStackImmediate ().

Murat
fonte
0

Eu notei algo muito interessante. Eu tenho no meu aplicativo a opção de abrir a galeria do telefone e o dispositivo pergunta qual aplicativo usar; clique na área cinza da caixa de diálogo e vi esse problema. Percebi como minha atividade passa de onPause, onSaveInstanceState de volta para onResume, e isso não acontece com onCreateView. Estou fazendo transações no onResume. Então, o que acabei fazendo é definir um sinalizador sendo negado em Pausa, mas sendo verdadeiro emCriarView. se o sinalizador for verdadeiro onResume, execute onCommit, caso contrário, commitAllowingStateLoss. Eu poderia continuar perdendo muito tempo, mas queria verificar o ciclo de vida. Eu tenho um dispositivo que é o sdkversion 23 e não entendi esse problema, mas eu tenho outro que tem 21 anos e lá estou.

Juan Mendez
fonte
-1

você pode usar FragmentActivity.onStart antes de popBackStackImmediate

como isso:

public void backStackFragment() {
    this.start();
    getFragmentManager().popBackStackImmediate();
}

public void start(){
    FragmentActivity a = getActivity();
    if(a instanceof DepositPlanPadActivity){
      ((DepositPlanPadActivity)a).onStart();
    }
    if(a instanceof SmallChangePlanPad){
            ((SmallChangePlanPad)a).onStart();
        }
        if(a instanceof UserCenterActivity){
            ((UserCenterActivity)a).onStart();
        }
    }

http://jorryliu.blogspot.com/2014/09/illegalstateexception-can-not-perform.html

user3327339
fonte