Activity reinicia quando configuração muda (SCREEN ORIENTATION)

4 respostas
gatyuri

Estou com um problema na minha aplicação em que toda vez que altero uma configuração da activity, como por exemplo a orientation da tela, a activity reinicia, eu não teria problema se na oncreate da activity eu não tivesse as funções que iniciam os handlers da aplicação.

Minha ideia é alterar dentro da aplicação a orientation manualmente (PORTRAIT/LANDSCAPE), usando a funcão setRequestedOrientation, que nas documentaçoes do android diz que funciona da seguinte forma:

Change the desired orientation of this activity. If the activity is currently in the foreground or otherwise impacting the screen orientation, the screen will immediately be changed (possibly causing the activity to be restarted). Otherwise, this will be used the next time the activity is visible

Mas toda vez que altero, a aplicação reinicia, ou melhor, a activity reinicia, já me deram a ideia de bloquear esta operacao automatica implementando no manifest.xml a seguinte linha de codigo dentro da activity: android:configChanges="orientation", e assim ficaria por conta do usuário a implementacao do que seria feito, e dentro da funcao onConfigurationChanged(), mas não é recomendado e não é seguro.

Então parti para outra possível solução, a função onRetainNonConfigurationInstance(), que retorna um objeto, como o próprio nome já diz, ela salva um objeto toda vez que a activity é destruída, podendo ser recuperada na próxima inicialização pela função getLastNonConfigurationInstance().
Entao fiz o seguinte no meu onCreate:

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); // Inicio a aplicação bloqueando o rotacionamento automatico pelo sensor
        boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; // Aqui é só para alterar o tamanho do frame buffer quando estiver em portrait ou landscape
        int frameBufferWidth = isLandscape ? 320 : 240;
        int frameBufferHeight = isLandscape ? 240 : 320;
        Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth,
                frameBufferHeight, Config.RGB_565);
        
        float scaleX = (float) frameBufferWidth
                / getWindowManager().getDefaultDisplay().getWidth();
        float scaleY = (float) frameBufferHeight
                / getWindowManager().getDefaultDisplay().getHeight();
        //Aqui eu inicializo tudo o que vai monitorar a aplicação
        renderView = new AndroidFastRenderView(this, frameBuffer);
        graphics = new AndroidGraphics(getAssets(), frameBuffer);
        fileIO = new AndroidFileIO(this);
        audio = new AndroidAudio(this);
        input = new AndroidInput(this, renderView, scaleX, scaleY);
        
        // Logo entao eu inicializo o objeto da tela com o ultimo objeto salvo pela onRetain..()
        screen = (Screen) getLastNonConfigurationInstance();
        if (screen == null) { // Porem se nada foi salvo, a getLastNonConfigurationIstance() retorna um null
            screen = getStartScreen(); // E entao eu inicio a primeira tela
        }
        setContentView(renderView);
        
        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame");
    }

E na função onRetainNonConfigurationInstance() implementei o seguinte:

@Override
    public Object onRetainNonConfigurationInstance() {
    	return screen;
    }

Basicamente já deveria funcionar normalmente, mas não é isso o que acontece, toda vez que mando rotacionar manualmente, a activity reinicia o objeto aparentemente é recuperado mas como a classe screen depende das classes AndroidGraphics, AndroidAudio etc a tela não inicializa corretamente.

A função que manda rotacionar esta implementada na mesma classa da activity e é a seguinte:

@Override
    public void setOrientation(int preferenceOrientation) {
        if (preferenceOrientation == Configuration.ORIENTATION_LANDSCAPE) { 
            if( getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
        } else if (preferenceOrientation == Configuration.ORIENTATION_PORTRAIT) {
            if( getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            }    
        } else {
            if( getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
            }
        }
    }

Se a opção rotacionar automático do aparelho estiver habilitada e removo a função setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); do oncreate, toda vez que o aparelho é rotacionado a tela gira, mas não recupera as informações da classe Screen.

Enfim, só quero poder rotacionar a tela sem que tudo seja reinicializado novamente, alguem se habilita?

4 Respostas

wagnerfrancisco

Eu não conheço muito estas classes de manipulação de áudio/vídeo, mas baseado no problema, creio que vc poderia:

  1. Retornar os objetos dependentes (graphics, audio, etc.) no método onRetainNonConfigurationInstance (por meio de um Map).
  2. Ao invés de retornar screen diretamente, retornar os atributos que precisa para reconstruí-lo.

Acho que a opção 2, se possível, seria melhor…

A

Olá
Quando você opta por controlar a mudança de orientação da tela “manualmente” você deve implementar o método onConfigurationChanged(),
e lá incluir o tratamento da alteração.
Lembre-se, o onCreate só será executado uma vez, quando ocorrer a mudança de orientação da tela a activity não será destruída e recriada.
Os objetos instanciados no onCreate não serão destruídos.
É sua responsabilidade restaurar os componentes da tela.
Não é recomendado salvar objetos relacionados com views, drawables, adapters, e outros relacionados com o contexto. Por causa de “vazamento de memória”.
Outra coisa, o reinício da activity não é causado apenas pela mudança de orientação da tela…
Ou seja, é complicado!

Abraço

gatyuri

wagnerfrancisco:
Eu não conheço muito estas classes de manipulação de áudio/vídeo, mas baseado no problema, creio que vc poderia:

  1. Retornar os objetos dependentes (graphics, audio, etc.) no método onRetainNonConfigurationInstance (por meio de um Map).
  2. Ao invés de retornar screen diretamente, retornar os atributos que precisa para reconstruí-lo.

Acho que a opção 2, se possível, seria melhor…

Então, primeiramente eu tentei retornar os objetos dependentes por meio de um map, mas na hora de recuperar objetos e instanciar dava errro, pois por algum motivo esses obejtos se “perdiam”. Logo tentei a opção 2, junto com a dica do A H Gusukuma:

A H Gusukuma:
Olá
Quando você opta por controlar a mudança de orientação da tela “manualmente” você deve implementar o método onConfigurationChanged(),
e lá incluir o tratamento da alteração.
Lembre-se, o onCreate só será executado uma vez, quando ocorrer a mudança de orientação da tela a activity não será destruída e recriada.
Os objetos instanciados no onCreate não serão destruídos.
É sua responsabilidade restaurar os componentes da tela.
Não é recomendado salvar objetos relacionados com views, drawables, adapters, e outros relacionados com o contexto. Por causa de “vazamento de memória”.
Outra coisa, o reinício da activity não é causado apenas pela mudança de orientação da tela…
Ou seja, é complicado!

Abraço

E implementei o método onConfigurationChanged para tratar a recuperação da tela, agora esta funcionando tudo perfeitamente como era para funcionar.

EXTREMAMENTE GRATO PELA AJUDA! :smiley:

Marky.Vasconcelos

Só uma observação, o método getLastNonConfigurationInstance está depreciado nas APIs recentes e é marcado como final no pacote de support do Android.

Hoje em dia eu guardo o estado em um retained fragment.

Criado 7 de outubro de 2012
Ultima resposta 9 de out. de 2012
Respostas 4
Participantes 4