Intent: passando objetos com campos não serializáveis

Suponha a seguinte classe Aluno:

public class Aluno {
    private String nome;
    private String telefone;

    public Aluno(String nome, String telefone) {
        this.nome = nome;
        this.telefone = telefone;
    }
}

Como faríamos para passar uma instância dessa classe para uma Activity?

Como visto nos cursos de Android da Caelum, poderiamos usar o método putExtra() da Intent para isso:

Aluno aluno = new Aluno("Felipe", "555-1234");

Intent intent = new Intent(this, OutraActivity.class);
intent.putExtra("aluno", aluno);
startActivity(intent);

Porém, o putExtra() não é capaz de receber um Aluno! Então, temos que implementar a interface Serializable:

public class Aluno implements Serializable {
    //...
}

Agora a classe Aluno é capaz de serializar todos os seus atributos que podem ser serializáveis. Porém, o que acontece se a classe Aluno possuir algum atributo que não é serializável?

Um exemplo disso seria colocar um atributo Context na nossa classe para exibir um Toast:

public class Aluno {
    private String nome;
    private String telefone;
    private Context context = CaixaPreta.getContext();

    public Aluno(String nome, String telefone) {
        this.nome = nome;
        this.telefone = telefone;
    }

    public void mostraNome() {
        Toast.makeText(context, this.nome, Toast.LENGTH_LONG).show();
    }
}

Como Context não é serializável, teremos uma java.io.NotSerializableException ao chamar o putExtra()!

Para resolver esse problema, podemos simplesmente ignorar o campo Context na serialização por meio do modifier transient:

public class Aluno {
    private String nome;
    private String telefone;
    private transient Context context = CaixaPreta.getContext();

    public Aluno(String nome, String telefone) {
        this.nome = nome;
        this.telefone = telefone;
    }

    public void mostraNome() {
        Toast.makeText(context, this.nome, Toast.LENGTH_LONG).show();
    }
}

Como poderemos chamar o método mostraNome() após recuperar o aluno na OutraActivity?

 
Intent intent = getIntent();
Aluno recuperado = (Aluno) intent.getSerializableExtra("aluno");

recuperado.mostraNome();

Como ignoramos o campo Context, ao recuperar o aluno o conteúdo de context será null.

À primeira vista, poderíamos popular o context no construtor do Aluno:

public Aluno(String nome, String telefone) {
    this.nome = nome;
    this.telefone = telefone;
    this.context = CaixaPreta.getContext();
}

Porém, o processo de desserialização não chama o construtor da classe. Então ainda teremos um null no context.

Ao fazer a desserialização do nosso aluno podemos implementar um método chamado readObject, responsável por popular os campos do objeto desseralizado.

public class Aluno {
    private String nome;
    private String telefone;
    private transient Context context = CaixaPreta.getContext();

    public Aluno(String nome, String telefone) {
        this.nome = nome;
        this.telefone = telefone;
    }

    public void mostraNome() {
        Toast.makeText(context, this.nome, Toast.LENGTH_LONG).show();
    }

	private void readObject(ObjectInputStream in) 
					throws IOException, ClassNotFoundException {
		in.defaultReadObject();
		this.context = CaixaPreta.getContext();
	}
}

Com isso, podemos passar o Aluno para uma outra Activity e repopular o campo que não é serializável!

3 Comentários

  1. Daniel Baccin 24/09/2014 at 11:49 #

    Ola Felipe, muito bacana o post, Parabéns! Mas me surgiu a seguinte duvida… Se meu Aluno tiver uma propriedade do tipo enum que por sua vez tem uma propriedade chamada descricaoAmigavel. Pelo que sei, o valor a ser serializado será o valor absoluto do enum. Como faço para chegar até a descricaAmigavel ?? (Com Vraptor, utilizo Converter para isso) Vlw.

  2. Tiago 04/12/2014 at 14:37 #

    Muito Bom!

    Pena que já passei por isso e tive de contornar.
    Também é possível implementar a interface Parcelable no lugar da Serializable.
    Esta é propria do Android, tem uma implementação mais complexa, porém é mais rápida.

  3. Etni 03/06/2015 at 11:08 #

    Muito bom mesmo!
    aborda várias dificuldades que podemos encontrar, explicando todo o contexto do código, parabéns!

Deixe uma resposta