객체 직렬화 대신 Parcelable 사용의 이점


97

내가 이해 Bundle하고 ParcelableAndroid가 직렬화를 수행하는 방식에 속합니다. 예를 들어 활동간에 데이터를 전달하는 데 사용됩니다. 하지만 Parcelable예를 들어 비즈니스 개체의 상태를 내부 메모리에 저장하는 경우 클래식 직렬화 대신 사용하면 어떤 이점이 있는지 궁금합니다 . 고전적인 방법보다 더 간단하거나 빠를까요? 클래식 직렬화를 사용해야하는 곳과 번들을 사용하는 것이 더 좋은 곳은 어디입니까?

답변:


99

"Pro Android 2"에서

참고 : Parcelable을 보면 Android가 내장 된 Java 직렬화 메커니즘을 사용하지 않는 이유는 무엇입니까? Android 팀은 Java의 직렬화가 Android의 프로세스 간 통신 요구 사항을 충족하기에는 너무 느리다는 결론에 도달했습니다. 그래서 팀은 Parcelable 솔루션을 구축했습니다. Parcelable 접근 방식을 사용하려면 클래스의 멤버를 명시 적으로 직렬화해야하지만 결국 객체의 직렬화가 훨씬 빨라집니다.

또한 Android는 데이터를 다른 프로세스로 전달할 수있는 두 가지 메커니즘을 제공합니다. 첫 번째는 인 텐트를 사용하여 활동에 번들을 전달하는 것이고, 두 번째는 서비스에 Parcelable을 전달하는 것입니다. 이 두 메커니즘은 상호 교환 할 수 없으며 혼동해서는 안됩니다. 즉, Parcelable은 활동에 전달되지 않습니다. 활동을 시작하고 일부 데이터를 전달하려면 번들을 사용하십시오. Parcelable은 AIDL 정의의 일부로 만 사용됩니다.


9
"Pro Android 2"가 무엇인가요?
AlikElzin-kilaka 2012

79
두 번째 단락은 사실이 아닙니다. 번들을 사용하여 Parcelable을 매개 변수로 활동에 전달할 수 있습니다.
Ixx

3
객체를 직렬화 할 때 getBundle메소드를 생성 한 다음 writeToParcelas 에서 호출 dest.writeBundle(getBundle());하면 객체에서 자동으로 두 옵션을 사용할 수 있습니다. 라이브 객체가 주목 대한 흥미 소포 특징이 있습니다 developer.android.com/reference/android/os/Parcel.html
mikebabcock

2
@lxx : 왜 하나가 번들을 통해 활동에 parcelable 개체를 전달해야하는지 궁금합니다. 그렇게하면 IMO가 불필요하게 하나 이상의 직렬화 수준을 추가하고 다른 것은 없습니다.
상승

4
Philippe Breault는 이에 대한 멋진 기사를 작성하고 성능 테스트도 추가했습니다. developerphil.com/parcelable-vs-serializable
WonderCsabo

23

SerializableAndroid에서는 우스꽝스럽게 느립니다. 실제로 많은 경우에 쓸모없는 경계선.

Parcel그리고 Parcelable놀랍도록 빠르지 만, 그 문서 는 안드로이드의 다른 버전에 따라 구현이 다르기 때문에 스토리지에 대한 범용 직렬화에 사용해서는 안된다고 말합니다.

합리적인 속도로 데이터를 스토리지에 직렬화하는 문제에 대한 최상의 솔루션은 직접 롤링하는 것입니다. 나는 개인적으로 유사한 인터페이스를 Parcel가지고 있고 모든 표준 유형을 매우 효율적으로 직렬화 할 수 있는 내 자신의 유틸리티 클래스 중 하나를 사용 합니다 (유형 안전성을 희생하면서). 여기에 요약 된 버전이 있습니다.

public interface Packageable {
    public void readFromPackage(PackageInputStream in)  throws IOException ;
    public void writeToPackage(PackageOutputStream out)  throws IOException ; 
}


public final class PackageInputStream {

    private DataInputStream input;

    public PackageInputStream(InputStream in) {
        input = new DataInputStream(new BufferedInputStream(in));
    }

    public void close() throws IOException {
        if (input != null) {
            input.close();
            input = null;
        }       
    }

    // Primitives
    public final int readInt() throws IOException {
        return input.readInt();
    }
    public final long readLong() throws IOException {
        return input.readLong();
    }
    public final long[] readLongArray() throws IOException {
        int c = input.readInt();
        if (c == -1) {
            return null;
        }
        long[] a = new long[c];
        for (int i=0 ; i<c ; i++) {
            a[i] = input.readLong();
        }
        return a;
    }

...

    public final String readString()  throws IOException {
        return input.readUTF();
    }
    public final <T extends Packageable> ArrayList<T> readPackageableList(Class<T> clazz) throws IOException {
        int N = readInt();
        if (N == -1) {
            return null;
        }
        ArrayList<T> list = new ArrayList<T>();
        while (N>0) {
            try {
                T item = (T) clazz.newInstance();
                item.readFromPackage(this);
                list.add(item);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            N--;
        }
        return list;
    }

}



public final class PackageOutputStream {

    private DataOutputStream output;

    public PackageOutputStream(OutputStream out) {
        output = new DataOutputStream(new BufferedOutputStream(out));
    }

    public void close() throws IOException {
        if (output != null) {
            output.close();
            output = null;
        }
    }

    // Primitives
    public final void writeInt(int val) throws IOException {
        output.writeInt(val);
    }
    public final void writeLong(long val) throws IOException {
        output.writeLong(val);
    }
    public final void writeLongArray(long[] val) throws IOException {
        if (val == null) {
            writeInt(-1);
            return;
        }
        writeInt(val.length);
        for (int i=0 ; i<val.length ; i++) {
            output.writeLong(val[i]);
        }
    }

    public final void writeFloat(float val) throws IOException {
        output.writeFloat(val);
    }
    public final void writeDouble(double val) throws IOException {
        output.writeDouble(val);
    }
    public final void writeString(String val) throws IOException {
        if (val == null) {
            output.writeUTF("");
            return;
        }
        output.writeUTF(val);
    }

    public final <T extends Packageable> void writePackageableList(ArrayList<T> val) throws IOException {
        if (val == null) {
            writeInt(-1);
            return;
        }
        int N = val.size();
        int i=0;
        writeInt(N);
        while (i < N) {
            Packageable item = val.get(i);
            item.writeToPackage(this);
            i++;
        }
    }

}

2
이 사용자 정의 클래스를 사용하는 것과 Externalizable 인터페이스를 구현하고 동일한 작업을 수행하는 것의 차이점은 무엇입니까?
Carrotman42

1
번들은 필드 이름도 직렬화합니다. 수천 개의 개체에는 적합하지 않습니다.
Reuben Scratton 2012 년

1
죄송합니다, 또 다른 시리얼을 발명하는 것은 짜증 - 지금은 아직 처리하는 또 다른 "Parcelable"가있다. 선택할 수있는 라이브러리가 많이 있습니다 (라이브러리는 검사, 테스트 및 다른 사람들이 사용하는 형식을 사용한다는 점입니다) : ProtocolBuffers, JSON, XML 등.이 점에서 Android 라이브러리가 정말 안타까운 일입니다. .

2
나는 서두 문장이 더 이상 사실이라고 생각하지 않는다 (작성된 지 5 년 후). 나는 오랫동안 문제없이 Java 직렬화를 사용하고 있습니다. 방금 작성한 블로그 게시물에서이 주제에 대해 잠재적으로 흥미로운 내용을 찾을 수 있습니다. nemanjakovacevic.net/blog/english/2015/03/24/...
네마냐 Kovacevic

1
네, 더 이상 그런 문제가 아닙니다. 몇 년 동안 Serializable (최적화 된 readObject / writeObject 구현 포함) 만 사용했습니다. 사실 저는 불과 며칠 전에 직렬화 된 몇 개의 객체의 16 진 덤프를보고 너무 낭비가 아니라는 점에 만족했습니다.
Reuben Scratton 2015 년


11

즉, 저장 목적으로 직렬화가 필요하지만 직렬화 가능 인터페이스로 인한 반사 속도 저하를 방지 하려면 Externalizable 인터페이스를 사용하여 고유 한 직렬화 프로토콜을 명시 적으로 만들어야합니다 .

제대로 구현되면 Parcelable의 속도와 일치하며 Android 및 / 또는 Java 플랫폼의 여러 버전 간의 호환성을 고려합니다.

이 기사는 다음과 같은 사항을 정리할 수도 있습니다.

Java에서 직렬화 가능과 외부화 가능의 차이점은 무엇입니까?

참고로 Kryo, Avro, Protocol Buffers 및 Jackson (json)을 제치고 많은 벤치 마크에서 가장 빠른 직렬화 기술이기도합니다.

http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking


7

요즘에는 그 차이가 그다지 눈에 띄지 않는 것 같습니다. 적어도 자신의 활동 사이에서 실행할 때는 아닙니다.

이 웹 사이트 에 표시된 테스트에 따르면 Parcelable은 최신 기기 (예 : 넥서스 10)에서 약 10 배, 오래된 기기 (예 : desire Z)에서는 약 17 배 더 빠릅니다.

그래서 그만한 가치가 있는지 결정하는 것은 당신에게 달려 있습니다.

비교적 작고 단순한 클래스의 경우 Serializable이 괜찮고 나머지는 Parcelable을 사용해야합니다.


차이가 줄어들고 있다고 말하는 당신이 돈에 맞다고 생각합니다. 그러나 직렬화 가능을 사용하는 것이 마샬링 된 바이트 배열의 크기 측면에서 훨씬 더 효율적일 수 있으며 TransactionTooLargeException을 피하는 데 도움이 될 수 있음을 발견했습니다. 나는이 (내)에 귀하의 의견을 듣고 싶어요 포스트 블로그 것 nemanjakovacevic.net/blog/english/2015/03/24/...
네마냐 Kovacevic

글쎄, 당신은 정적 변수에 거대한 메모리 소비 객체를 넣고 그것을 가져올 때 (예를 들어 onCreate에서) null로 설정할 수 있습니다. 단점은 다중 프로세스를 지원하지 않으며이를 수행하는 방법이 조금 더럽다는 것입니다. 큰 비트 맵을 전달하려는 경우에 해당하는지 궁금합니다.
안드로이드 개발자

4

Parcelable 주로 사용 IPC 관련된 바인더 데이터로 전달 인프라 소포 .

Android는 전부는 아니지만 대부분의 IPC 작업에 Binder를 많이 사용하므로 필요한 경우 객체를 다른 프로세스로 전달할 수 있으므로 대부분의 위치, 특히 프레임 워크에서 Parcelable을 구현하는 것이 좋습니다. 그것은 개체를 "이동 가능"하게 만듭니다.

그러나 직렬화 가능을 광범위하게 사용하여 객체 상태를 저장하고 파일 시스템에 저장하기 만하면되는 비 Android 관련 비즈니스 계층이 있다면 직렬화 가능이 괜찮다고 생각합니다. Parcelable 상용구 코드를 피할 수 있습니다.


어떤 예에서 saya 파일 시스템에 실제 객체를 저장하고 싶습니까? 단순히 객체 내용을 가져와 실제 내용을 파일에 저장하지 않는 이유는 무엇입니까? 예를 들어 JSON 또는 xml을 살펴보십시오. 객체를 JSON 또는 XML 형식으로 저장할 수 있으며, POJO / Entity 유형의 객체는 주로 해당 상태에 대한 상태와 게터 및 세터로 구성된 일반적인 데이터 객체를 구성합니다. 그렇게하면 객체를 저장하기위한 목적으로 객체를 직렬화 할 필요가 없습니다. 관심있는 것은 객체 상태
Jonathan


1

GSON-> Serialise to JSON String-> Restore Object from JSON String을 사용합니다.


이것은 작은 개체에 적합하며 각각에 많은 속성을 가진 개체의 큰 배열이있는 경우에는 그다지 좋지 않습니다.
Nickmccomb

0

또한 Parcelable은 사용자가 writeToParcel ()을 재정 의하여 자신의 각 개체를 분할 할 수있는 사용자 지정 구현을 제공합니다. 그러나 직렬화는 데이터 전달 방식이 JAVA 리플렉션 API를 포함하므로이 사용자 지정 구현이 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.