Contenido sobre Android
Buscar
Social
Ofertas laborales ES
« Diseñando aplicaciones multi-plataforma en HTML con PhoneGap | Main | Google Chrome llega a Android »
jueves
feb092012

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());
        }
    }
}

References (1)

References allow you to track sources for this article, as well as articles that were written in response to this article.

Reader Comments (7)

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

mayo 7, 2012 | Unregistered Commentergonzalo

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.

mayo 7, 2012 | Registered Commenterjtristan

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.

mayo 8, 2012 | Unregistered Commenterwkats

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.

agosto 1, 2012 | Unregistered CommenterDaniel

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.

agosto 7, 2012 | Unregistered CommenterDaniel

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?

mayo 1, 2013 | Unregistered Commenteradelosri

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?

julio 29, 2013 | Unregistered CommenterJerry

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>