Android e l'interfaccia Parcelable

di  Antonio Coschignano, venerdì 07 settembre 2012

Nello sviluppare applicazioni android, quando abbiamo la necessità di trasferire un oggetto creato da noi da un'activity ad un'altra (oppure anche in altri contesti) dobbiamo ricorrere all'implementazione delle interfacce Parcelable e Parcelable.Creator. L'interfaccia Parcelable serve a registrare l'oggetto che stiamo trasferendo tramite il metodo writeToParcel() all'interno di un Parcel. Mentre l'interfaccia Parcelable.Creator serve a ricostruire l'oggetto.

Interfaccia Parcelable

L'interfaccia Parcelable descrive una modalità di registrazione di un oggetto con tutti i suoi dati primitivi tipo int, boolean, String - oppure un qualsiasi oggetto che a sua volta implementa Parcelable - in un oggetto Parcel associato ad esso. I metodi da implementare sono:

  • void writeToParcel(Parcel parcel, int flags): questo metodo riceve un oggetto Parcel dove andremo a trasferire gli attributi del nostro oggetto
  • int describeContents: questo metodo in genere ritorna zero. Esso viene utilizzato in alcuni casi particolari che non tratteremo in questo articolo

Parcelable Creator

Le classi che implementano l'interfaccia Parcelable devono anche avere un campo statico chiamato CREATOR, che è un oggetto che implementa l'interfaccia Parcelable.Creator. Questo oggetto serve a ricostruire la classe che implementa Parcelable. I metodi di questa interfaccia sono:

  • public Point createFromParcel(Parcel source):questo metodo viene invocato automaticamente nel momento in cui si tenta di ricostruire un ipotetico oggetto Parcelable. Un caso molto comune lo troviamo nella classe Bundle quando invochiamo il metodo getParcelable().
  • public Point[] newArray(int size): in questo caso viene ricostruito l'array di un oggetto parcelable. Tutto ci sarà piu chiaro nell'esempio che farema fra poco.

Da notare che l'oggetto Parcel di cui ci serviamo per leggere e scrivere dati, contiene tutti i metodi di lettura e scrittura dei dati primitivi. Ecco l'elenco:

  • writeBooleanArray (boolean []) , readBooleanArray (boolean []) , createBooleanArray()
  • writeByteArray (byte []) , writeByteArray (byte [], int, int) , readByteArray (byte []) , createByteArray ()
  • writeCharArray (char []) , readCharArray (char []) , createCharArray ()
  • writeDoubleArray (double []) , readDoubleArray (double []) , createDoubleArray ()
  • writeFloatArray (float []) , readFloatArray (float []) , createFloatArray ()
  • writeIntArray (int []) , readIntArray (int []) , createIntArray ()
  • writeLongArray (long []) , readLongArray (long []) , createLongArray ()
  • writeStringArray (String []) , readStringArray (String []) , createStringArray ()
  • writeSparseBooleanArray (SparseBooleanArray) , readSparseBooleanArray ()

Esempio con Parcelable

Vediamo un esempio concreto per chiarirci meglio le idee. Definiamo ad esempio una classe Point che rappresenta un punto con coordinate x e y. Dobbiamo trasferirlo da un Activity ad un'altra tramite l'oggetto Intent:

import android.os.Parcel;
import android.os.Parcelable;

public class Point implements Parcelable{

    private int x;
    private int y;

    public Point(int x, int y) {
       this.x = x;
       this.y = y;
    }

    public Point(Parcel parcel) {
       this.x = parcel.readInt();
       this.y = parcel.readInt();
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
       this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
       this.y = y;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(x);
        dest.writeInt(y);
    }

    public final static Parcelable.Creator CREATOR = new Parcelable.Creator() {
        @Override
        public Point createFromParcel(Parcel source) {
            return new Point(source);
        }

        @Override
        public Point[] newArray(int size) {
            return new Point[size];
        }
    };
}

Ho evidenziato in grassetto i metodi e costruttori di cui stiamo parlando. L'interfaccia Parcelable.Creator invoca il costruttore (che deve essere presente) Point(Parcel p) per ricostruire l'oggetto quando viene invocato getParcelable() sia da un Intent oppure da un Bundle. Come vedete l'implementazione è stata fatta tramite una inner class. Adesso vediamo un esempio di trasferimento dell'oggetto Point:

Intent intent = new Intent(ActivityA.this, ActivityB.class);
Bundle bundle = new Bundle();
bundle.putParcelable("punto", new Point(4,3));
intent.putExtras(bundle);
startActivity(intent);

Abbiamo creato un oggetto Point e lo abbiamo incapsulato tramite un Bundle che attraverso il metodo putParcelable() viene registrato nel Parcel. Quindi viene invocato implicitamente il metodo writeToParcel().

E' importante rispettare sia nel costruttore che nel metodo writeToParcel() l'ordine della scrittura che della lettura dei dati primitivi:

...

public Point(Parcel parcel) {
   this.x = parcel.readInt(); // 1
   this.y = parcel.readInt(); // 2
}

...

public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(x); // 1
    dest.writeInt(y); // 2
}

Adesso mettiamo il caso di trovarci nell'ActivityB e dobbiamo leggere l'oggetto trasferito tramite l'Intent:

public class ActivityB {

    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
    	...
    	Intent intent = getIntent();
    	Bundle bundle = intent.getExtras();
    	Point p = (Point)bundle.getParcelable("punto");

    }
	....
}

L'esempio è abbastanza chiaro, attraverso il casting otteniamo l'oggetto che abbiamo trasferita dall'ActivityA. Esistono altri metodi della classe Bundle, per trasferire array di Parcelable, come nel nostro caso array di Point. In questo caso possiamo utilizzare il metodo get/putParcelableArray() o anche ArrayList di Parcelable con i rispettivi metodi get/putParcelableArrayList().

L'utilizzo di Parcelable è un'alternativa alla serializzazione degli oggetti. In Android possiamo comunque ricorrere alla serializzazione però ciò comporta un calo delle prestazioni e quindi non ne è consigliato l'utilizzo.

Altri link che potrebbero interessarti
  • » Struttura di un'applicazione Android
  • » Screenshot su un dispositivo Android
  • » Pubblicare applicazioni nell'Android Market
  • » Integrare Google Analytics nelle applicazioni Android
  • » Integrare AdMob nelle applicazioni Android
  • » Guida all'installazione dell'Android SDK
  • » Guida agli Adapter e le ListView in Android
  • » Gestire Thread e GUI nelle applicazioni Android
  • » Applicazioni Android ed il supporto multilingue
  • » Android VideoView e le Youtube Api
  • » Android Linkify e i collegamenti testuali
  • » Android HTML TextView
  • » Android e tecniche multithreading
  • » Android e la gestione dei sensori