ประโยชน์ของการใช้ Parcelable แทนการทำให้เป็นอนุกรมวัตถุ


97

ตามที่ฉันเข้าใจBundleและParcelableเป็นของวิธีที่ Android ดำเนินการทำให้เป็นอนุกรมซึ่งใช้เป็นตัวอย่างในการส่งผ่านข้อมูลระหว่างกิจกรรมต่างๆ แต่ฉันสงสัยว่ามีประโยชน์ในการใช้Parcelableแทนการทำให้อนุกรมแบบคลาสสิกในกรณีที่บันทึกสถานะของวัตถุทางธุรกิจของฉันลงในหน่วยความจำภายในหรือไม่? จะง่ายกว่าหรือเร็วกว่าวิธีคลาสสิก? ฉันควรใช้การทำให้เป็นอนุกรมแบบคลาสสิกที่ไหนและควรใช้บันเดิลที่ไหนดีกว่า

คำตอบ:


99

จาก "Pro Android 2"

หมายเหตุ: การดู Parcelable อาจทำให้เกิดคำถามทำไม Android ถึงไม่ใช้กลไกการทำให้เป็นอนุกรม Java ในตัว ปรากฎว่าทีม Android ได้ข้อสรุปว่าการทำให้เป็นอนุกรมใน Java นั้นช้าเกินไปที่จะตอบสนองความต้องการในการสื่อสารระหว่างกระบวนการของ Android ทีมงานจึงสร้างโซลูชัน Parcelable วิธีการแบบ Parcelable ต้องการให้คุณจัดลำดับสมาชิกในชั้นเรียนของคุณอย่างชัดเจน แต่ในท้ายที่สุดคุณจะได้รับการจัดลำดับของวัตถุได้เร็วขึ้น

โปรดทราบด้วยว่า Android มีกลไกสองอย่างที่ช่วยให้คุณส่งข้อมูลไปยังกระบวนการอื่นได้ อันดับแรกคือการส่งแพ็กเกจไปยังกิจกรรมโดยใช้ความตั้งใจและอย่างที่สองคือการส่งพัสดุไปยังบริการ กลไกทั้งสองนี้ไม่สามารถใช้แทนกันได้และไม่ควรสับสน นั่นคือ Parcelable ไม่ได้หมายถึงการส่งต่อไปยังกิจกรรม หากคุณต้องการเริ่มต้นกิจกรรมและส่งต่อข้อมูลให้ใช้ชุดข้อมูล Parcelable มีขึ้นเพื่อใช้เป็นส่วนหนึ่งของนิยาม AIDL เท่านั้น


9
"Pro Android 2" คืออะไร
AlikElzin-kilaka

79
ย่อหน้าที่สองไม่เป็นความจริงคุณสามารถส่ง Parcelable เป็นพารามิเตอร์ไปยังกิจกรรมโดยใช้บันเดิล ...
Ixx

3
เมื่อฉันจัดลำดับวัตถุของฉันฉันจะสร้างgetBundleวิธีการจากนั้นเรียกสิ่งนั้นจากwriteToParcelเป็นdest.writeBundle(getBundle());และฉันมีทั้งสองตัวเลือกที่มีอยู่ในวัตถุโดยอัตโนมัติ มีคุณลักษณะพัสดุที่น่าสนใจสำหรับวัตถุสดที่ระบุไว้ที่นี่: developer.android.com/reference/android/os/Parcel.html
mikebabcock

2
@lxx: ฉันสงสัยว่าทำไมต้องส่งของที่สามารถส่งพัสดุผ่านบันเดิลไปยังกิจกรรมได้ IMO หากคุณทำเช่นนั้นคุณกำลังเพิ่มการทำให้เป็นอนุกรมอีกระดับหนึ่งโดยไม่จำเป็น & ไม่มีอะไรอื่น
เพิ่มขึ้น

4
Philippe Breault ได้สร้างบทความดีๆเกี่ยวกับเรื่องนี้และยังเพิ่มการทดสอบประสิทธิภาพอีกด้วย developerphil.com/parcelable-vs-serializable
WonderCsabo

23

Serializableช้าอย่างตลกขบขันบน Android เส้นขอบไร้ประโยชน์ในหลาย ๆ กรณีในความเป็นจริง

ParcelและParcelableรวดเร็วอย่างน่าอัศจรรย์ แต่เอกสารระบุว่าคุณต้องไม่ใช้เพื่อการจัดลำดับตามวัตถุประสงค์ทั่วไปในการจัดเก็บเนื่องจากการใช้งานจะแตกต่างกันไปตาม Android เวอร์ชันต่างๆ (เช่นการอัปเดตระบบปฏิบัติการอาจทำให้แอปที่ต้องใช้งานไม่ได้)

ทางออกที่ดีที่สุดสำหรับปัญหาในการจัดลำดับข้อมูลไปยังหน่วยเก็บข้อมูลด้วยความเร็วที่เหมาะสมคือการม้วนข้อมูลของคุณเอง โดยส่วนตัวแล้วฉันใช้หนึ่งในคลาสยูทิลิตี้ของฉันเองซึ่งมีอินเทอร์เฟซที่คล้ายกัน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
อะไรคือความแตกต่างระหว่างการใช้คลาสที่กำหนดเองของคุณกับการใช้อินเทอร์เฟซภายนอกที่ปรับแต่งได้และทำสิ่งเดียวกัน
Carrotman42

1
Bundle ทำให้ชื่อฟิลด์เป็นอนุกรมเช่นกัน ... มันไม่เหมาะกับวัตถุหลายพันชิ้น
Reuben Scratton

1
ขออภัยประดิษฐ์ยัง serializer อีกครับ - ตอนนี้มีเพียงอีกหนึ่ง "Parcelable" ที่จะจัดการกับ มีให้เลือกมากมายพร้อมไลบรารี (ความแตกต่างคือไลบรารีได้รับการตรวจสอบทดสอบและใช้รูปแบบที่คนอื่นใช้): ProtocolBuffers, JSON, XML และอื่น ๆ เป็นเรื่องน่าเสียดายที่ไลบรารี Android แย่มากในเรื่องนี้ .

2
ฉันไม่คิดว่าประโยคเปิดจะเป็นความจริงอีกต่อไป (5 ปีหลังจากสร้าง) ฉันใช้ java serialization โดยไม่มีปัญหาใด ๆ มานานแล้ว คุณสามารถค้นหาสิ่งที่น่าสนใจในหัวข้อนี้ได้ในโพสต์บล็อกที่ฉันเพิ่งเขียน nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

1
ใช่มันไม่ใช่ปัญหาอีกต่อไปแล้ว ฉันไม่ได้ใช้อะไรมากไปกว่า Serializable (ด้วยการใช้งาน readObject / writeObject ที่ปรับให้เหมาะสม) มาหลายปีแล้ว ในความเป็นจริงฉันดูการถ่ายโอนข้อมูลฐานสิบหกของวัตถุที่ต่อเนื่องกันสองสามวันเมื่อไม่กี่วันที่ผ่านมาและพอใจว่ามันไม่สิ้นเปลืองเกินไป
Reuben Scratton


11

หากคุณต้องการการทำให้เป็นอนุกรมเพื่อวัตถุประสงค์ในการจัดเก็บเช่น แต่ต้องการหลีกเลี่ยงการลงโทษความเร็วของการสะท้อนที่เกิดขึ้นจากอินเทอร์เฟซแบบอนุกรมคุณควรสร้างโปรโตคอลการทำให้เป็นอนุกรมของคุณเองอย่างชัดเจนด้วยอินเทอร์เฟซภายนอกที่ปรับแต่งได้

เมื่อนำไปใช้อย่างถูกต้องสิ่งนี้จะตรงกับความเร็วของ Parcelable และยังพิจารณาถึงความเข้ากันได้ระหว่าง Android เวอร์ชันต่างๆและ / หรือแพลตฟอร์ม Java

บทความนี้อาจทำให้ชัดเจนขึ้นเช่นกัน:

อะไรคือความแตกต่างระหว่าง Serializable และ Externalizable ใน Java?

ในด้านข้างมันเป็นเทคนิคการทำให้เป็นอนุกรมที่เร็วที่สุดในการวัดประสิทธิภาพหลายตัวโดยเอาชนะ Kryo, Avro, Protocol Buffers และ Jackson (json):

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


7

ดูเหมือนว่าความแตกต่างจะไม่เป็นที่สังเกตเห็นได้ชัดเจนในปัจจุบันอย่างน้อยก็ไม่ใช่เมื่อคุณเรียกใช้ระหว่างกิจกรรมของคุณ

จากการทดสอบที่แสดงในเว็บไซต์นี้ Parcelable เร็วขึ้นประมาณ 10 เท่าในอุปกรณ์รุ่นใหม่ล่าสุด (เช่น nexus 10) และเร็วกว่า 17 เท่าสำหรับอุปกรณ์รุ่นเก่า (เช่นปรารถนา Z)

ดังนั้นจึงขึ้นอยู่กับคุณที่จะตัดสินใจว่าคุ้มหรือไม่

สำหรับชั้นเรียนที่ค่อนข้างเล็กและเรียบง่ายสามารถต่ออนุกรมได้และสำหรับส่วนที่เหลือคุณควรใช้ Parcelable


ฉันคิดว่าคุณคิดถูกแล้วที่บอกว่าเงินส่วนต่างหดหายไป อย่างไรก็ตามฉันพบว่าการใช้แบบอนุกรมได้นั้นมีประสิทธิภาพมากกว่าในแง่ของขนาดของอาร์เรย์ไบต์แบบมาร์แชลและสามารถช่วยหลีกเลี่ยง TransactionTooLargeException ได้ ฉันชอบที่จะรับฟังความคิดเห็นของคุณในบล็อกโพสต์ (ของฉัน) nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

คุณสามารถใส่อ็อบเจ็กต์ที่ใช้หน่วยความจำขนาดใหญ่บนตัวแปรคงที่และตั้งค่าเป็น null ได้ทันทีเมื่อคุณดึงข้อมูล (บน onCreate เป็นต้น) ข้อเสียคือไม่รองรับหลายกระบวนการและเป็นวิธีที่ค่อนข้างสกปรก สงสัยว่าเป็นกรณีนี้หรือไม่หากคุณต้องการส่งผ่านบิตแมปขนาดใหญ่
นักพัฒนา Android

4

Parcelable เกี่ยวข้องกับ IPC เป็นหลักโดยใช้โครงสร้างพื้นฐานBinderซึ่งข้อมูลจะถูกส่งผ่านเป็นพัสดุพัสดุภัณฑ์

เนื่องจาก Android ใช้ Binder เป็นส่วนใหญ่ถ้าไม่ใช่ทั้งหมดงาน IPC จึงเหมาะสมที่จะใช้ Parcelable ในสถานที่ส่วนใหญ่และโดยเฉพาะอย่างยิ่งในกรอบงานเนื่องจากอนุญาตให้ส่งผ่านวัตถุไปยังกระบวนการอื่นได้หากคุณต้องการ ทำให้วัตถุ "เคลื่อนย้ายได้"

แต่ถ้าคุณมีชั้นธุรกิจที่ไม่ใช่ Android โดยเฉพาะซึ่งใช้ serializables อย่างกว้างขวางเพื่อบันทึกสถานะของอ็อบเจ็กต์และจำเป็นต้องจัดเก็บไว้ในระบบไฟล์เท่านั้นฉันคิดว่าการทำให้เป็นอนุกรมนั้นใช้ได้ อนุญาตให้หลีกเลี่ยงรหัสแผ่นหม้อไอน้ำแบบพัสดุได้


ในตัวอย่างใดที่คุณต้องการจัดเก็บออบเจ็กต์จริงในระบบไฟล์ saya ทำไมไม่เพียงแค่รับเนื้อหาวัตถุและจัดเก็บเนื้อหาจริงในไฟล์ ดูตัวอย่าง JSON หรือแม้แต่ xml คุณสามารถบันทึกอ็อบเจ็กต์ในรูปแบบ JSON หรือ XML ซึ่งเป็นอ็อบเจ็กต์เช่นเดียวกับประเภท POJO / เอนทิตีที่สร้างอ็อบเจ็กต์ข้อมูลทั่วไปที่สร้างโดยส่วนใหญ่เป็น States และ getters และ setters สำหรับสถานะนั้น ด้วยวิธีนี้คุณไม่จำเป็นต้องทำให้วัตถุเป็นอนุกรมเพื่อจุดประสงค์ในการจัดเก็บเพราะสิ่งที่คุณสนใจคือสถานะของวัตถุ
Jonathan

1

อ้างอิงจากบทความนี้http://www.mooproductions.org/node/6?page=5 Parcelable น่าจะเร็วกว่า

ไม่ได้กล่าวถึงในบทความนี้คือฉันไม่คิดว่าอ็อบเจ็กต์ที่ต่ออนุกรมได้จะทำงานใน AIDL สำหรับบริการระยะไกล


1

ฉันเพิ่งใช้ GSON -> Serialise เป็น JSON String -> กู้คืนวัตถุจาก JSON String


สิ่งนี้ใช้ได้ดีสำหรับวัตถุขนาดเล็กไม่มากนักเมื่อคุณมีอาร์เรย์ของวัตถุขนาดใหญ่ที่มีคุณสมบัติมากมายในแต่ละอัน
Nickmccomb

0

นอกจากนี้ Parcelable ยังนำเสนอการใช้งานแบบกำหนดเองซึ่งผู้ใช้มีโอกาสที่จะจัดเก็บวัตถุแต่ละชิ้นของตนโดยการแทนที่ writeToParcel () อย่างไรก็ตามการทำให้เป็นอนุกรมไม่ใช่การใช้งานแบบกำหนดเองนี้เนื่องจากวิธีการส่งผ่านข้อมูลเกี่ยวข้องกับ JAVA reflect API

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.