Paso de parámetros entre actividades en Android. La interfaz Parcelable.
En android, si necesitamos pasar datos entre nuestras actividades, tendremos que indicar a nuestro Intent que datos desemos pasar. Para ello, disponemos de los métodos putExtra(String name, String value). En el parámetro name le indicaremos el nombre que deseamos dar al valor que vamos a pasar, con el formato del prefijo del paquete.
Para ver un ejemplo, vamos a crear un programa que va a representar un pedido, mediante una clase de cabecera y una lista de clases de líneas. Vamos a pasar de nuestra primera actividad a la segunda el id de un pedido, la dirección a la que se tiene que enviar y el número de líneas que tiene.
Intent intent = new Intent(this, SegundaPantallaActivity.class); intent.putExtra("com.jtristan.parcelable.ID_PEDIDO", pedido.getId()); intent.putExtra("com.jtristan.parcelable.DIRECCION", pedido.getDireccion()); intent.putExtra("com.jtristan.parcelable.NUMERO_LINEAS", pedido.getLineas().size()); startActivity(intent);
En la actividad “SegundaPantallaActivity” recuperamos los valores creándonos un objeto de la clase Bundle. Esta clase nos provee un método get para cada tipo de dato que podemos recuperar.
Bundle extra = this.getIntent().getExtras(); pedido.setId(extra.getLong("com.jtristan.parcelable.ID_PEDIDO")); pedido.setDireccion(extra.getString("com.jtristan.parcelable.DIRECCION")); int numero_lineas_pedido = extra.getInt("com.jtristan.parcelable.NUMERO_LINEAS");
Como podemos observar en la documentación de Intent, estamos trabajando sólo con tipos primitivos y arrays de los mismos. ¿Qué pasa si necesitamos pasar un objeto?. ¿No podemos pasar nuestros objetos pedidos?. Podríamos serializar nuestras clases implementando la interfaz java.io.Serializable. Sin embargo, la serialización en android supone un gran problema de rendimiento. Por ello, android dispone de la interfaz Parcelable. Es una interfaz específica para “serializar” nuestros objetos y poder pasarlos entre las actividades, optimizando el rendimiento.
Vamos a ver como podemos implementar la interfaz Parcelable para poder pasar un pedido junto con sus líneas entre dos actividades. Para que el ejemplo sea más completo, vamos a poder almacenas tanto una lista de pedidos como un único pedido en la cabecera del pedido.
private Long id; private String nombre; private String direccion; private boolean servido; private LineaPedido linea; private ArrayList lineas; public CabeceraPedido(){ linea = new LineaPedido(); lineas = new ArrayList(); } //Omitimos los get y los set public CabeceraPedido(Parcel parcel){ this(); readToParcel(parcel); } public static final Parcelable.Creator CREATE = new Parcelable.Creator() { public CabeceraPedido createFromParcel(Parcel parcel) { return new CabeceraPedido(parcel); } public CabeceraPedido[] newArray(int size) { return new CabeceraPedido[size]; } };
En primer lugar nos creamos un constructor para la “serialización de la clase”. Vamos a llamar a este constructor cuando queramos recuperar los valores. Como podéis ver, en el constructor llamamos al constructor CabeceraPedido() para instanciar la Lista y el objeto Pedido.
Cuando implementamos la interfaz Parcelable, necesitamos declararnos una variable estática de tipo Parcelable.Creator que se llamará CREATOR y que va a ser la encargada de implementar la interfaz Parcelable.Creator. Esta interfaz tiene dos métodos, mediante los cuales vamos a poder devolver los valores que previamente hemos almacenado.
Para almacenar los datos tenemos que implementar el método writeToParcel(Parcel dest, int flags). La clase Parcel nos provee métodos específicos para cada uno de los valores que vamos a poder almacenar.
public void writeToParcel(Parcel parcel, int flags) { parcel.writeLong(id); parcel.writeString(nombre); parcel.writeString(direccion); parcel.writeString(String.valueOf(servido)); parcel.writeTypedList(lineas); parcel.writeParcelable(linea, flags); }
Para poder “serializar” la lista de pedidos utilizaremos el método writeTypedList (List val)
Para ello, tenemos que hacer que la clase LineaPedido implemente también Parcelable.
Si quisiesemos pasar simplemente un objeto Línea, utilizaremos writeParcelable(Parcelable p, int parcelableFlags).
Para leer los datos, disponemos de los métodos read.
public void readToParcel(Parcel parcel){ String servido; id = parcel.readLong(); nombre = parcel.readString(); direccion = parcel.readString(); servido = parcel.readString(); if (servido.equals("true")) this.servido = true; else this.servido = false; parcel.readTypedList(lineas, LineaPedido.CREATOR); linea = parcel.readParcelable(LineaPedido.class.getClassLoader()); }
Como habréis podido observar no disponemos de un método específico para los booleanos. Cuando vayamos a recuperar los datos tendremos que aplicar nosotros la conversión.
Es fundamental, que leamos los datos en el mismo orden en que les hemos escrito.
Finalmente, en nuestra clase Activity, una vez declarado el intent pasaremos el objeto con putExtra(String name, Parcelable value). Funciona igual que cuando vamos a pasar un tipo primitivo como hemos visto al comienzo del artículo.
Intent intent = new Intent(this, SegundaPantalla.class); intent.putExtra("com.jtristan.parcelable.PEDIDO", pedido); startActivity(intent);
Al cargar la segunda actividad recuperamos los valores mediante un objeto Bundle.
public class SegundaPantallaActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CabeceraPedido pedido = new CabeceraPedido(); LineaPedido linea = new LineaPedido(); ArrayList lineas = new ArrayList(); Bundle extra = this.getIntent().getExtras(); pedido.setId(extra.getLong("com.jtristan.parcelable.ID_PEDIDO")); pedido.setDireccion(extra.getString("com.jtristan.parcelable.DIRECCION")); int numero_lineas_pedido = extra.getInt("com.jtristan.parcelable.NUMERO_LINEAS"); Log.i("PEDIDO", pedido.getId().toString() + " " + pedido.getDireccion() + " " + String.valueOf(numero_lineas_pedido)); pedido = extra.getParcelable("com.jtristan.parcelable.PEDIDO"); Log.i("PEDIDO", pedido.getId().toString() + " " + pedido.getNombre() + " " + pedido.getDireccion() + " " + pedido.isServido()); lineas = pedido.getLineas(); for (int i=0; i<lineas.size();i++){ linea = lineas.get(i); Log.i("LINEA", linea.getId().toString() + " " + linea.getMaterial() + " " + linea.getCantidad().toString() + " " + linea.getPrecio().toString()); } } }
Reader Comments (8)
la verdad es que la programación es algo muy dificil, pero los pasos que describes en este tutorial y las fotos que adjuntas en el post son muy buenas, y ayudan muchisimo, muchas gracias
Me alegro que te haya gustado.Tuve que pasar mi rato pegándome, sobre todo con las listas, así que me alegra ver que es útil.
Un saludo.
Interesante, me compré un libro de Android y ando en búsquedas por internet para complementar; el libro plantea los tipos de datos que se pueden pasar, incluyendo Arrays, pero como lo esperaba, no se puede pasar un Objeto de una clase que hagamos :/ lástima.
Felicitarte por el tutorial, te lo has currado bastante y me has ayudado a mí de una duda que tenía con ellos.
Si en tu clase tienes atributos del tipo String, Int, ... ya hay clases para hacer los "read" ó los "write" pero... ¿ con qué función leo y escribo si tengo un atributo del tipo Calendar (en mi caso) o un objeto de una clase creada ( como LineaPedido).
Lo resolví de la siguiente manera:
Para readParcel utilicé: fecha_ped = pedido_parcel.readParcelable(Calendar.class.getClassLoader());
Para writeParcel utilicé: dest.writeParcelable((Parcelable) fecha_ped, flags);
Y funciona perfectamente. Gracias por compartir.
Quiero corregir mi post anterior. No es correcto. Me funcionaba perfectamente porque en el constructor no inicializaba la variable Calendar, y por tanto, no transcribía los datos realmente.
La clase Calendar implementa la interfaz "serializable" por lo cual podemos hacerlo como si de un objeto serializable fuera:
Write--> fecha = parcel.readSerializable()
Read--> parcel.writeSerializable(fecha).
Gracias y disculpad por el error. Un saludo.
Excelente la explicacion y muchas gracias
Como es un poco confuso la teroria, no la explicación, pienso que una forma mas facil de lograr la persistencia sería escribir el objeto a SQLite antes de abandonar la actividad_1 y en la activida_2 leer de SQLite, por ser un motor ligero, rapido, esto debe tener buen desempeño
Que opinan?
Pero que tal si quiero pasar datos de un broadcastreceiver a un servicio? se puede?, actualmente utilizo esto apra recibir datos:
Bundle extra = this.getIntent().getExtras();, pero me marca error en getIntent.
Alguna solucion para esto?
Tengo una pantalla de Login en la que pido el correo electronico, logré pasar ese dato a una segunda activida la cual es la main (es un navigation drawer), en este main puse en el nav_header el correo, dentro del main esta el area de contenido y ahí tengo un image botton que manda a otro activity que tiene una flecha de retorno(no es un toolbar propio, solo le coloque la flecha a la tool bar por defecto. Como podria hacer para que al momento de apretar la flecha y regrese a la actividad padre (main) y muestre el correo donde lo puse? De la forma en que lo tengo se detiene la app al momento de apretar el image botton, agradeceria la ayuda.