12
respostas

Erro ao rotacionar o celular e tentar carregar fragment

Estou com um problema, quando eu rotaciono o celular seja de portrait pra landscape como o contrario e tento carregar um novo fragment, eu tomo uma exception "java.lang.IllegalStateException: Activity has been destroyed" eu uso o seguinte codigo:

 public void showMessage(final String messageToShow) {
               if (!isFinishing()) {
                   final MessageFragment messageFragment = new MessageFragment();
                   Bundle bundle = new Bundle();
                   bundle.putString("messageToShow", messageToShow);
                   messageFragment.setArguments(bundle);
                   FragmentManager fragmentManager = getSupportFragmentManager();
                   FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

                   fragmentTransaction.replace(R.id.container_fragment, messageFragment, "message");
                   fragmentTransaction.commitAllowingStateLoss();


               }



    }

Detalhe importante, quando esse código é executado após a rotação de tela, ja há um Fragment carregado do mesmo tipo "MessageFragment", e o erro é retornado somente quando chamo esse método depois da rotação, ou seja o celular ja esta com outra rotação e depois de alguns segundos ele é chamado. Desde ja agradeço.

12 respostas

Oi Luccas!!

Cada vez que você rotaciona o aparelho, o app destrói a activity e cria novamente na orientação desejada - landscape/portrait. O problema é que o fragment sabe quando a activity é criada, mas não sabe quando é destruída.

Verifica essas duas coisas:

  1. Se seu onCreate() tem o super.onCreate() (se não tiver, coloca).

  2. Acrescenta uma condição no if:

    if (!isFinishing() && !isDestroyed()) {

Me diz se funcionou!! Qualquer dúvida estamos a disposição!!!

Bons estudos!!

Ola Tais!

Meu OnCreate esta com o .super e eu ja uso o

if (!isFinishing() && isDestroyed())

No entanto eu gostaria de abrir o fragment nessa nova tela que foi criada, o que não estou conseguindo

Luccas,

Você pode nos passar a stacktrace e o código dessa activity para analisar melhor? ;)

É uma activity que faz bastante coisa então vou colocar o codigo mais importante aqui

public class MainActivity extends BaseActivity implements  Response {
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_payment);
        toolbar = (Toolbar) findViewById(R.id.toolbar);

        toolbar.setTitle("");
        ((TextView) toolbar.findViewById(R.id.toolbar_title)).setText("MainActivity");
        setSupportActionBar(toolbar);
        if (null == savedInstanceState) {
                loadInitialFragment(null);
        new AsyncInitial.execute(this);

        }


    }

protected void loadInitialFragment(final Boolean isIn) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                if (!isFinishing()) {
                    FragmentManager fragmentManager = getSupportFragmentManager();
                    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                    animateTransaction(fragmentTransaction, isIn);
                    InitialFragment initial = new InitialFragment();
                    fragmentTransaction.replace(R.id.container_fragment, initial, "initial");
                    fragmentTransaction.commitAllowingStateLoss();
                }

            }
        });
    }

public void showMessage(final String messageToShow) {
               if (!isFinishing()) {
//é um fragment simples com um campo de texto no centro
                   final MessageFragment messageFragment = new MessageFragment();
                   Bundle bundle = new Bundle();
                   bundle.putString("messageToShow", messageToShow);
                   messageFragment.setArguments(bundle);
                   FragmentManager fragmentManager = getSupportFragmentManager();
                   FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

                   fragmentTransaction.replace(R.id.container_fragment, messageFragment, "message");
                   fragmentTransaction.commitAllowingStateLoss();


               }



    }


//listener Response é chamado quando a asyncTask do oncreate termina sua rotina
  @Override
    public void onActionOk(String message) {
    //O erro ocorre quando esse metodo é chamado depois da rotacao da tela, mas ele ja tinha sido chamado uma vez antes da rotacao e aberto normalmente
    showMessage(message);
}

}

Muito Obrigado pela atenção

Luccas,

e a stacktrace que ele acusa? Pode nos mandar?

Tinha entendido errado, stack trace:

java.lang.IllegalStateException: Activity has been destroyed
                                                                                at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1864)
                                                                                at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:650)
                                                                                at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:614)
                                                                                at br.com.generic.ui.activity.MainActivity$7.run(MainActivity.java:308)
                                                                                at android.os.Handler.handleCallback(Handler.java:746)
                                                                                at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                                at android.os.Looper.loop(Looper.java:148)
                                                                                at android.app.ActivityThread.main(ActivityThread.java:5443)
                                                                                at java.lang.reflect.Method.invoke(Native Method)
                                                                                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
                                                                                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)

O método isDestroyed só está disponível a partir da API 17..

O problema pode ser solucionado instanciando o fragmentManager antes e usando-o na condição:

public void showMessage(final String messageToShow) {
    FragmentManager fragmentManager = getSupportFragmentManager();       
    if (!fragmentManager.isDestroyed()) {
                   final MessageFragment messageFragment = new MessageFragment();
                   Bundle bundle = new Bundle();
                   bundle.putString("messageToShow", messageToShow);
                   messageFragment.setArguments(bundle);
                   FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

                   fragmentTransaction.replace(R.id.container_fragment, messageFragment, "message");
                   fragmentTransaction.commitAllowingStateLoss();
               }

Verifica se assim o problema é resolvido.

Realmente o IsDestroyed funciona e evita que o app crashe, no entanto isso resolve um dos problemas, pois eu gostaria de poder continuar abrindo os fragments mesmo após a rotação da tela, então eu teria que fazer alguma coisa no else desse "!isDestroyed" que possibilitasse isso. Obrigado pela ajuda até agora, mas não sei o que pode ser feito

Luccas,

Você pode disponibilizar seu projeto no Github para a gente analisar e te ver direitinho o que pode estar acontecendo? Acho que assim a gente pode te ajudar melhor ;)

Então, é um projeto da minha empresa e não posso passar o o projeto. Não existe uma forma de abrir um fragment novamente após eu validar o isdestroyed?

Luccas,

em nossa app não colocamos nada no else. Ao recriar a activity, o fragment é criado.

public class Classe {

public void mostraFragment() {
    FragmentManager fragmentManager = getSupportFragmentManager();

    if(!fragmentManager.isDestroyed()) {
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        Bundle bundle = new Bundle();
        //continua o código...
    }
...
}

Se for uma opção, você pode usar um dos repositórios privados, como GitLab, e deletar o projeto depois.

Isso não é possível, infelizmente. Só para eu entender, vocês não abrem mais fragments se cai no else? A solução paliativa que achei, foi bloquear a rotação dele da forma que ele abriu a Activity, ou seja, se ele entrou com a orientação portrait ficara assim ate o final do procedimento e, também com o landScape. Além de adicionar o isDestroyed, porem dessa forma não cai no else.