(안드로이드) Parcelable

*Serializable 과 Parcelable의 차이점

참고: http://aroundck.tistory.com/2477



이 Parcelable의 뜻은 소포 또는 꾸러미란 뜻이다.
이 Parcelable는 Interface로, 이 녀석을 구현한 Class는 Parcel로 쓰여질 수도 있고, Object로 복구될 수도있다. Process간 정보 교환시 포장 압축하여 전달하고, 받으면 포장을 해제하는 것을, Interface로 만든것이라 보면 된다.

이 Parceleabe을 구현한 Class는 CREATOR라는 static field를 "반드시" 가지고 있어야 한다.
이 CREATOR라는 녀석은 Parcelable.Creator interface를 구현한 놈으로, 해당 Parcel이 어떻게 restore되는지 기술하고 있다.

이 Parcelable은 IPC,즉 프로세스간 통신에도 아주 유용하게 쓰인다. 이 녀석을 구현한 class는 IPC시 추가 작업 없이 process사이를 넘어 다닐 수 있따 물론 Serializable보다 빠르다.



[Parcel이라는 녀석은 무엇인가?]


IPC시 이 꾸러미를 다른 프로세스로 휙 던져주는 방식이다. Parcelable class가 IPC를 위해서 포장, 압축이 된 형태라고 볼 수 있다.

Developer를 참조하여 부가 설명을 하자면....

Parcel은 IBinder를 통해 전달 될 수 있는 Data나 Object Reference의 Container입니다.
이 Parcel은 primitive type, primitive array type 또는 Parcelable class들을 가지고 있을 수 있습니다.
이 Parcel은 Serialization과 "맥락"은 비슷하지만 "매커니즘"은 다르다.
이 Parcelable은 IPC에서 high performance를 낼 수 있는 형태로 되어 있다.


[만드는 방법]

1. 먼저 Parcelable 하지 않은 class에 Parcelable implemenets를 더해준다.

2. 기본적으로 public int describeContents()와 public void writeToParcel(Parcel dest,int flags)를 구현해 주어야 한다.

describeContents()는 Parcel의 내용을 기술해 주는 녀석인데, 보통 잘 쓰이지 않아 return 0;을 해준다.

writeToParcel(Parcel dest,int flags)는 Parcel 즉 상자안에 내용물을 넣는 작업을 해준다.
이 때 주의해야 할 것은 쓰는 순서이다. Parcel을 unmarshalling할 때 쓴 순서대로 읽어와야 하기 때문이다.

3. Developer에 기술된 데로, static으로 정의된 CREATOR field가 필요하다.
이 CREATOR는 Parcelable.Creator<T> type으로 생성되며, 선언과 동시에 초기화(Initialize)되어야 한다.
T는 제네릭 타입으로 Parcelable을 구현하는 class를 넣어주면 된다.
이 CREATOR라는 녀석은 unmarsahlling을 할 때 Parcelable class를 생성해주는 역할을 한다.

createFromParcel(Parcel source)는 Parcel이 된 Parcelable class를 restore하는 작업을 써주게 된다.
여기서 값을 read하는 순서는 writeToParcel에서 write한 순서와 동일하게 해야한다.
마지막으로 restore된 class를 return해주어야 한다.


newArray(int size)는 Parcel.createTypeArray()함수를 호출했을때 불리며,
해당 parcelable로 구성된 array를 return하기 때문에 
return new T[size];면 보통 충분하다.




[예제]


public class Rect{
     public int left;
     public int right;
     public int top;
     public int bottom;
}

<Parcelable implement>

public class Rect implements Parcelable{
 
     public int left;
     public int right;
     public int top;
     public int bottom;

public int describeContents(){
   return 0;
}

//쓰는 순서 주의
public void writeToParcel(Parcel dest, int flags){

 dest.writeInt(left);
 dest.writeInt(right);
 dest.writeInt(top);
 dest.writeInt(bottom);
}

//반드시 필요한, static field 로 정의된 CREATOR object
public static final Parcelable.Creator<Rect> CREATOR=new Parcelable.Creator<Rect>(){

      //읽는 순서에 주의
      public Rect createFromParcel(Parcel source){
             Rect r=new Rect();
             r.left=source.readInt();
             r.right=source.readInt();
             r.top=source.readInt();
             r.bottom=source.readInt();
             return r;
        }

     public Rect[] newArray(int size){
            return new Rect[size];
        }
    }
}


[Parcelable를 구현할 때 주의점(ArrayList)]


public class InnerData implements Parcelable{
 

     public int nSeq;
     public String strMessage;

     public InnerData(int nSeq, String strMessage){
           this.nSeq=nSeq;
           this.strMessage=strMessage;
     }

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

     @Override
     public void writeToParcel(Parcel dest,int flags){
         dest.writeInt(nSeq);
         dest.writeString(strMessage);
     }

    private InnerData(Parcel source){
          nSeq=source.readInt();
          strMessage=source.readString();
     }

     public static final Parcelable.Creator<InnerData> CREATOR=new Parcelable.Creator<InnerData>(){

     @Override
      public InnerData createFromParcel(Parcel source){
             return new InnerData(Source);
     }

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

위의 클래스를 자식클래스로 사용하는 ArrayList를 포함한 클래스는 아래와 같이 작성한다.

public class TestListData implements Parcelable{
    
      public ArrayList<InnerData> alData;
    
      public TestListData(){
         alData=new ArrayList<InnderData>();
       }

      @Override
      public int describeContents(){
              return 0;
     }
        @Override
        public void writeToParcel(Parcel dest){
           //이 부분에서 writeTypedList()를 사용해야 한다.
            dest.writeTypedList(alData);
        }

        private TestListData(Parcel source){
             //이 부분도 중요하다.
           alData=new ArrayList<InnerData>();
           source.readTypedList(alData,InnerData.CREATOR);
         }

       public static final Parcelabe.Creator<TestListData> CREATOR=new Parcelable.Creator<TestListData>(){

  @Override
  public TestListData createFromParcel(Parcel source){
      return new TestListData(source);
   }

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

source.readTypeList(alData,InnerData.CREATOR);부분에서 주의할 부분은 이 메소드를 실행하기 전에 꼭 ArrayList데이터를 메모리에 할당해야 하는 것이다. 안하면 NullPointerException이 발생한다.


문장의 의미를 살펴보면 alData라는 변수에 TypedList를 읽어오라는 것이고, ArrayList에 사용된 데이터 타입은 InnderData클래스 형태라는 것을 알려주고, 해당 클래스의 CREATOR를 이용하여 데이터를 생성하여 전달받기 위함이다.


      



참고:http://aroundck.tistory.com/57
참고: http://dante2k.tistory.com/443

댓글

이 블로그의 인기 게시물

(네트워크)폴링방식 vs 롱 폴링방식

(ElasticSearch) 결과에서 순서 정렬

(18장) WebSocekt과 STOMP를 사용하여 메시징하기