2011년 5월 18일 수요일

Parcelable Object 만들기 (2)

저번에는 아주 간단한 Rect 클래스로 parcelable하게 만들어 보았습니다.
이번에는 좀 더 복잡한 형태의 클래스를 가지고 parcelable하게 만들어 보도록 하죠.

이번에 parcelable하게 만들어 볼 클래스는 EyePoint 클래스 입니다.

public class EyePoint {
public Point left;
public Point right;

public class Point {
public int x;
public int y;
}
}

눈 좌표를 나타내는 클래스 인데, 왼쪽 눈과 오른쪽 눈 좌표를 가지고 있습니다.
그리고 다시 한쪽 눈 좌표는 x, y integer 값을 가지고 있죠.

뭐 물론 주우욱 풀어서 leftX, leftY, rightX, rightY 4개의 필드를 만들어서 표현 할 수도 있겠습니다만,
전 그냥 EyePoint 클래스 안에 다시 Point 클래스를 만들었습니다. 뭐 제 마음이죠.

그럼 일단 안쪽에 있는 EyePoint$Point 클래스 부터 parcelable하게 만들어 봅시다.

Step 1.

public class EyePoint implements Parcelable {
public Point left;
public Point right;



// EyePoint$Point Inner Class
public static class Point implements Parcelable {
public int x;
public int y;

public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(x);
dest.writeInt(y);
}
public static final Parcelable.Creator CREATOR = new Creator() {
public Point createFromParcel(Parcel source) {
Point p = new Point();
p.x = source.readInt();
p.y = source.readInt();
return p;
}
public Point[] newArray(int size) {
return new Point[size];
}
};
}
}

저번에 만들었던 Rect 클래스 보다 무려 필드의 수가 반 밖에 안됩니다.
이쯤이야 금방 만들 수 있지요...


한가지 주의할 점은 Point 클래스의 CREATOR가 static이니
Point 클래스도 static이 되어야 EyePoint를 통해서 접근 할 수 있는것이 좀 다른 점입니다.
어차피 Eclipse에서 다 잡아 주니까 부담없이 코딩 하시면 되겠습니다.


[Write Parcelable And Read Parcelable]

자... 그럼 이제 EyePoint 클래스를 parcelable하게 만들어 봅시다.
Point 타입의 필드가 left, right 두개가 있으니 이것을 Parcel에다가 써야 하는데...

어떤 타입으로 써야 할까요?

눈치가 빠르신 분들은 이미 writeParcelable() 메소드를 사용하면 될거라 생각 하셨을 겁니다.

이미 Point는 Parcelable Interface를 implements 했으니 이미 parcelable합니다.
그래서 writeParcelable() 메소드를 사용해서 Parcel에다 쓸 수 있는 것이죠.

그런데 writeParcelable() 메소드를 잘 살펴보면,

다른 Primitive 타입과는 다른 인자가 하나 더 붙어 있습니다.

public final void writeParcelable (Parcelable p, int parcelableFlags)

parcelableFlags의 정체는 데체 무어란 말인가...
근데 지금은 그냥 별 생각 없이 ZERO로 채워 주시면 됩니다. 아핫핫... 넘어갑시다.

Step 2.

public class EyePoint implements Parcelable {
public Point left;
public Point right;


public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(left, 0);
dest.writeParcelable(right, 0);
}


// EyePoint$Point Inner Class
public static class Point implements Parcelable {
public int x;
public int y;

public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(x);
dest.writeInt(y);
}
public static final Parcelable.Creator CREATOR = new Creator() {
public Point createFromParcel(Parcel source) {
Point p = new Point();
p.x = source.readInt();
p.y = source.readInt();
return p;
}
public Point[] newArray(int size) {
return new Point[size];
}
};
}
}

보시는 바와 같이 그냥 0을 썼습니다. 뭐 별거 없네요...

그럼 이제 Parcel에다 쓰는 부분이 끝났으니 CREATOR를 만들어 봅시다.


아까는 writeParcelable() 메소드를 사용 했으니

반대로 readParcelable() 메소드를 사용 하면 됩니다.


참 말은 쉽군요. 그럼 한번 살펴 봅시다.

public final T readParcelable (ClassLoader loader)

잉? 다른건 그냥 읽었는데 이건 ClassLoader가 필요하군요.
데체 이 ClassLoader는 쌩뚱맞게 어디서...

우리가 읽고 싶은것은 Point 타입입니다.
그래서 Point의 ClassLoader를 지정해 주어야지 Point 값을 읽어 올 수 있습니다.

ClassLoader를 얻어내는 방법은 의외로 간단합니다.
Intent를 쓸 때와 비슷한 방법으로 얻어 오면 되는 것이죠.

ClassLoader loader = EyePoint.Point.class.getClassLoader();

Intent를 쓸 때는 Class까지만 넘겨주면 되는 거였는데,

Class 객체에서 getClassLoader()를 호출하게 되면 ClassLoader를 쉽게 얻어 올 수 있습니다.

Step 3.

public class EyePoint implements Parcelable {
public Point left;
public Point right;


public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(left, 0);
dest.writeParcelable(right, 0);
}
public static final Parcelable.Creator CREATOR = new Creator() {
public EyePoint createFromParcel(Parcel source) {
EyePoint e = new EyePoint();

ClassLoader loader = EyePoint.Point.class.getClassLoader();
e.left = source.readParcelable(loader);
e.right = source.readParcelable(loader);

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


// EyePoint$Point Inner Class
public static class Point implements Parcelable {
public int x;
public int y;

public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(x);
dest.writeInt(y);
}
public static final Parcelable.Creator CREATOR = new Creator() {
public Point createFromParcel(Parcel source) {
Point p = new Point();
p.x = source.readInt();
p.y = source.readInt();
return p;
}
public Point[] newArray(int size) {
return new Point[size];
}
};
}
}

네.. 이런 것이죠.
이제 왠만한 클래스들은 parcelable하게 만들 수 있겠죠?


[Appendix]

네... 위에서도 그냥 지나 쳤던 문제의 parcelableFlags가 아직 남아 있죠...
역시 이것의 사용 용도에 대해서는 미궁입니다.

이것역시 integer값으로, bit mask 형식으로 사용하기 위한 인자 입니다.


지금 사용 가능한 플래그는

Parcelable.PARCELABLE_WRITE_RETURN_VALUE (0x00000001) 뿐입니다.

소스를 거의다 뒤져 보았는데, 이걸 사용하는 곳은 ParcelFileDescriptor 클래스 밖에 없었습니다.
대부분은 0으로 채워져 있었지요.

ParcelFileDescriptor는 또 데체 어디에 쓰이는건지 원...
보면 볼 수록 알 수 없는 Android 인 것 같습니다.

댓글 1개:

어린곰공주 :

어린곰공주

찾고 있던 정보 잘보고갑니다 :-)