Comportamento de contexto do Android AsyncTask

Estou trabalhando com o AsyncTasks no Android e estou lidando com um problema.

Tomemos um exemplo simples, uma Atividade com uma AsyncTask. A tarefa em segundo plano não faz nada de espetacular, apenas dorme por 8 segundos.

No final do AsyncTask no método onPostExecute (), estou apenas definindo um status de visibilidade do botão como View.VISIBLE, apenas para verificar meus resultados.

Agora, isso funciona muito bem até que o usuário decida alterar a orientação de seus telefones enquanto o AsyncTask está funcionando (dentro da janela de suspensão de 8 segundos).

Entendo o ciclo de vida da atividade do Android e sei que a atividade é destruída e recriada.

É aí que entra o problema. O AsyncTask está se referindo a um botão e, aparentemente, mantém uma referência ao contexto que iniciou o AsyncTask em primeiro lugar.

Eu esperaria que esse contexto antigo (desde que o usuário causou uma alteração na orientação) se tornasse nulo e o AsyncTask gere um NPE para a referência ao botão que ele está tentando tornar visível.

Em vez disso, nenhum NPE é lançado, o AsyncTask acha que a referência do botão não é nula, define-a como visível. O resultado? Nada está acontecendo na tela!

Atualizar: Eu lidei com isso mantendo umWeakReference para a atividade e alternar quando ocorrer uma alteração na configuração. Isso é complicado.

Aqui está o código:

public class Main extends Activity {

    private Button mButton = null;
    private Button mTestButton = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mButton = (Button) findViewById(R.id.btnStart);
        mButton.setOnClickListener(new OnClickListener () {
            @Override
            public void onClick(View v) {
                new taskDoSomething().execute(0l);
            }
        });
        mTestButton = (Button) findViewById(R.id.btnTest);   
    }

    private class TaskDoSomething extends AsyncTask<Long, Integer, Integer> 
    {
        @Override
        protected Integer doInBackground(Long... params) {
            Log.i("LOGGER", "Starting...");
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 0;
        }

        @Override
        protected void onPostExecute(Integer result) {
            Log.i("LOGGER", "...Done");
            mTestButton.setVisibility(View.VISIBLE);
        }
    }
}

Tente executá-lo e, enquanto o AsyncTask estiver funcionando, altere a orientação do telefone.

questionAnswers(4)

yourAnswerToTheQuestion