Implementar la Interface Parcelable en una clase en Android

Buenos días, hoy vamos a ver como implementar la interface Parcelable en nuestros proyectos Android.


Esta interface es usada por Android principalmente, para poder pasar objetos de una clase entre nuestras Activities, agregandolas al Intent

Esta interface es fácil de implementarlas y vamos a crear una clase con diferentes miembros de diferentes tipos para ver como se puede hacer.

  • Creación de una clase que implemente la interface

    Lo primero que haremos es crear una clase nueva, en mi caso la llamaré Model e implementará la interface Parcelable.

            
    public class Model implements Parcelable {
       private long unLong;
       private double unDouble;
       private float unFloat;
       private String unString;
       private boolean unBoolean;
       private OtraClase unParcelable;
    // Setters y getters
    }

    He agregado los tipos de datos mas comunes que se me han presentado en varios proyectos, por lo que tenemos un long, un double, un float, un String, un boolean y OtraClase que es una clase que a su vez también implementa la interface Parcelable.

  • Sustituir los métodos que necesitamos

    Para implementar la interface Parcelable tendremos que implementar una serie de métodos que vamos a necesitar como veremos a continuación

    @Override
            public int describeContents() {
                return 0;
            }
    
            @Override
            public void writeToParcel(Parcel dest, int flags) {
    
            }

    Además, deberemos implementar un campo static llamado CREATOR como vemos a continuación:

            public static final Parcelable.Creator<Model> CREATOR = new Parcelable.Creator<Model>() {
    
                @Override
                public Model createFromParcel(Parcel source) {
                    return null;
                }
    
                @Override
                public Model[] newArray(int size) {
                    return new null;
                }
    
            };
            

    Estos son los métodos o campos que deberemos agregar, y en el siguiente paso les aplicaremos lo necesario para que sea funcional.

  • Adaptar la implementación

    Ahora vamos a ver como podemos adaptar estos métodos y campos a nuestra clase:

            @Override
            public int describeContents() {
                return 0;
            }
    
            @Override
            public void writeToParcel(Parcel dest, int flags) {
                dest.writeLong(unLong);
                dest.writeDouble(unDouble);
                dest.writeFloat(unFloat);
                dest.writeString(unString);
                dest.writeByte((byte) (unBoolean == true ? 1 : 0));
                dest.writeParcelable(unParcelable, flags);
            }
    
            public static final Parcelable.Creator<Model> CREATOR = new Parcelable.Creator<Model>() {
    
                @Override
                public Model createFromParcel(Parcel source) {
                    return new Model(source);
                }
    
                @Override
                public Model[] newArray(int size) {
                    return new Model[size];
                }
    
             };
    
            private Model(Parcel source) {
                this.unLong = source.readLong();
                this.unDouble = source.readDouble();
                this.unFloat = source.readFloat();
                this.unString = source.readString();
                this.unBoolean = source.readByte() == 1 ? true : false;
                this.unParcelable = source.readParcelable(OtraClase.class
                .getClassLoader());
            }
            

    Vamos a ir parte por parte para comprender bien lo que está pasando:

    • writeToParcel(Parcel dest, int flags)

      Código

                      @Override
                      public void writeToParcel(Parcel dest, int flags) {
                          dest.writeLong(unLong);
                          dest.writeDouble(unDouble);
                          dest.writeFloat(unFloat);
                          dest.writeString(unString);
                          dest.writeByte((byte) (unBoolean == true ? 1 : 0));
                          dest.writeParcelable(unParcelable, flags);
                      }
                      

      Aquí vemos como “escribimos” en un objeto de la clase Parcel los elementos de nuestra clase.

    • CREATOR

      Código:

                      public static final Parcelable.Creator<Model> CREATOR = new Parcelable.Creator<Model>() {
      
                          @Override
                          public Model createFromParcel(Parcel source) {
                              return new Model(source);
                          }
      
                          @Override
                          public Model[] newArray(int size) {
                              return new Model[size];
                          }
      
                      };
                      

      Aquí vemos como implementamos el campo static que es necesario para implementar correctamente la interface. Lo que hacemos es sustituir los métodos createFromParcel que nos servirá para reconstruir nuestro objeto en base a un objeto de la clase Parcel. Para ello deberemos suministrar un constructor que reciba un argumento de la clase Parcel.

                         private Model(Parcel source) {
                              this.unLong = source.readLong();
                              this.unDouble = source.readDouble();
                              this.unFloat = source.readFloat();
                              this.unString = source.readString();
                              this.unBoolean = source.readByte() == 1 ? true : false;
                              this.unParcelable = source.readParcelable(OtraClase.class
                              .getClassLoader());
                         }
                         

      Cabe destacar, y es muy importante, debemos leerlos en el mismo orden en el cual los escribimos, ya que tiene un enfoque FIFO (first input first output).

    • OtraClase

      Nuestra otra clase que implementa esta interface es mas simple y podemos ver su código a continuación:

                      public class OtraClase implements Parcelable {
                          private long id;
      
                          // Setter y getter
      
                          @Override
                          public int describeContents() {
                              return 0;
                          }
      
                          @Override
                          public void writeToParcel(Parcel dest, int flags) {
                              dest.writeLong(id);
                          }
      
                          private OtraClase(Parcel source) {
                              this.id = source.readLong();
                          }
      
                          public static final Parcelable.Creator<OtraClase> CREATOR = new Parcelable.Creator<OtraClase>() {
      
                              @Override
                              public OtraClase createFromParcel(Parcel source) {
                                  return new OtraClase(source);
                              }
      
                              @Override
                              public OtraClase[] newArray(int size) {
                                  return new OtraClase[size];
                              }
      
                          };
      
                      }
                      
  • Probarlo en el emulador

    Ahora probamos todo esto en una Activity simple, el cual vemos a continuación su código:

            public class MainActivity extends Activity {
    
                @Override
                protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                    setContentView(R.layout.activity_main);
    
                    // Instanciamos nuestra Clase
                    final Model model = new Model();
    
                    // Asignamos Valores
                    model.setUnLong(10);
                    model.setUnDouble(10.0d);
                    model.setUnFloat(20.0f);
                    model.setUnString("Parcelable");
                    model.setUnBoolean(true);
    
                    final OtraClase otra = new OtraClase();
                    otra.setId(1000);
    
                    model.setUnParcelable(otra);
    
                    // Imprimimos su resultado en la consola
                    Model.printModel(model);
    
                    // Lanzamos la siguiente Activity donde comprobaremos los valores de nuevo
                    launchSecondActivity(model);
                }
    
                private void launchSecondActivity(Model model){
                    Intent intent = new Intent(this, SecondActivity.class);
                    intent.putExtra("PARCELABLE", model);
                    startActivity(intent);
                }
    
                @Override
                public boolean onCreateOptionsMenu(Menu menu) {
                    // Inflate the menu; this adds items to the action bar if it is present.
                    getMenuInflater().inflate(R.menu.main, menu);
                    return true;
                }
    
            }
            

    El código es bastante simple, solo creamos una instancia de nuestra clase, imprimimos el resultado y luego vamos a otra Activity que simplemente recibirá el objeto y lo imprimirá en el log.

            public class SecondActivity extends Activity {
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_second);
    
                // Obtenemos nuestro Parcelable desde el Intent
                final Model model = getIntent().getParcelableExtra("PARCELABLE");
    
                // Imprimimos sus valores
                Model.printModel(model);
                }
    
                @Override
                public boolean onCreateOptionsMenu(Menu menu) {
                    // Inflate the menu; this adds items to the action bar if it is present.
                    getMenuInflater().inflate(R.menu.second, menu);
                    return true;
                }
    
            }
            

    Como vemos, recibimos el objeto e imprimimos los valores, pudiendo ver en el LogCat lo siguiente:

            12-22 19:30:48.149: I/unLong(2199): El id es 10
            12-22 19:30:48.149: I/unDouble(2199): El double es 10.0
            12-22 19:30:48.157: I/unFloat(2199): El float es 20.0
            12-22 19:30:48.157: I/unString(2199): El String es Parcelable
            12-22 19:30:48.157: I/unBoolean(2199): El boolean es true
            12-22 19:30:48.157: I/unParcelable(2199): El id de OtraClase es 1000
            12-22 19:30:48.157: I/ActivityManager(482): START u0 {cmp=sekth.droid.parcelabletutorial/.SecondActivity (has extras)} from pid 2199
    
    
            12-22 19:30:48.261: I/unLong(2199): El id es 10
            12-22 19:30:48.261: I/unDouble(2199): El double es 10.0
            12-22 19:30:48.261: I/unFloat(2199): El float es 20.0
            12-22 19:30:48.261: I/unString(2199): El String es Parcelable
            12-22 19:30:48.261: I/unBoolean(2199): El boolean es true
            12-22 19:30:48.261: I/unParcelable(2199): El id de OtraClase es 1000
            

Esto es todo sobre una implementación básica de la interface Parcelable, pero en algunos casos se requerirá algo mas complejo y habrá que hacer alguna cosa mas especial.

El código de este ejemplo podemos encontrarlo disponible en GitHub

Sin más, cualquier aporte o corrección es bienvenido.

Saludos!!!