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

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:

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:

[code] 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");
}  [/code]

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?

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…

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

[quote=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…[/quote]

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:

[quote=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[/quote]

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:

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.