Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

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.