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!
Programação, Mobile, Front-end, Design & UX, Infraestrutura e Business
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.
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.
Muito bom mesmo!
aborda várias dificuldades que podemos encontrar, explicando todo o contexto do código, parabéns!