2012-04-09 7 views
64

Tôi đã theo lớp mà đọc và viết một mảng của các đối tượng từ/đến một bưu kiện:Đọc & viết mảng của Parcelable đối tượng

class ClassABC extends Parcelable { 
    MyClass[] mObjList; 

    private void readFromParcel(Parcel in) { 
     mObjList = (MyClass[]) in.readParcelableArray(
       com.myApp.MyClass.class.getClassLoader())); 
    } 

    public void writeToParcel(Parcel out, int arg1) { 
     out.writeParcelableArray(mObjList, 0); 
    } 

    private ClassABC(Parcel in) { 
     readFromParcel(in); 
    } 

    public int describeContents() { 
     return 0; 
    } 

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

     public ClassABC createFromParcel(Parcel in) { 
      return new ClassABC(in); 
     } 

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

Trong đoạn mã trên tôi nhận được một ClassCastException khi đọc readParcelableArray:

ERROR/AndroidRuntime (5880): Gây ra bởi: java.lang.ClassCastException: [Landroid.os.Parcelable;

Điều gì sai ở mã trên? Trong khi viết mảng đối tượng, trước tiên tôi có nên chuyển mảng thành ArrayList không?

UPDATE:

Is it OK để chuyển đổi một mảng Object để một ArrayList và thêm nó vào bưu kiện? Ví dụ, khi viết:

ArrayList<MyClass> tmpArrya = new ArrayList<MyClass>(mObjList.length); 
    for (int loopIndex=0;loopIndex != mObjList.length;loopIndex++) { 
     tmpArrya.add(mObjList[loopIndex]); 
    } 
    out.writeArray(tmpArrya.toArray()); 

Khi đọc:

final ArrayList<MyClass> tmpList = 
      in.readArrayList(com.myApp.MyClass.class.getClassLoader()); 
    mObjList= new MyClass[tmpList.size()]; 
    for (int loopIndex=0;loopIndex != tmpList.size();loopIndex++) { 
     mObjList[loopIndex] = tmpList.get(loopIndex); 
    } 

Nhưng bây giờ tôi nhận được một NullPointerException. Cách tiếp cận trên có đúng không? Tại sao nó ném một NPE?

+0

Loại mConversationMemberList là gì? – Chetan

+0

Tôi đã cập nhật câu hỏi, nó thuộc loại MyClass []. – User7723337

Trả lời

139

Bạn cần phải viết mảng bằng cách sử dụng phương pháp Parcel.writeTypedArray() và đọc nó trở lại với phương pháp Parcel.createTypedArray(), như vậy:

MyClass[] mObjList; 

public void writeToParcel(Parcel out) { 
    out.writeTypedArray(mObjList, 0); 
} 

private void readFromParcel(Parcel in) { 
    mObjList = in.createTypedArray(MyClass.CREATOR); 
} 

Lý do lý do tại sao bạn không nên sử dụng các phương pháp readParcelableArray()/writeParcelableArray() là kết quả là readParcelableArray() sẽ tạo ra kết quả là Parcelable[]. Điều này có nghĩa là bạn không thể truyền kết quả của phương thức đến MyClass[]. Thay vào đó, bạn phải tạo một mảng MyClass có cùng độ dài với kết quả và sao chép mọi phần tử từ mảng kết quả vào mảng MyClass.

Parcelable[] parcelableArray = 
     parcel.readParcelableArray(MyClass.class.getClassLoader()); 
MyClass[] resultArray = null; 
if (parcelableArray != null) { 
    resultArray = Arrays.copyOf(parcelableArray, parcelableArray.length, MyClass[].class); 
} 
+1

Cảm ơn, đã làm các trick! Điều này nên được làm rõ trong tài liệu hướng dẫn bởi vì phương pháp đặt tên làm cho nó âm thanh như một mảng Parcelable là những gì cần phải được viết, khi đó là rõ ràng hơn mã. – Tom

+0

Có, tên của phương thức này hơi khó hiểu. Khi tôi đối mặt với vấn đề này, cách duy nhất để giải quyết nó là đọc nguồn Android, đó là lý do tại sao tôi quyết định viết câu trả lời này. – Michael

+3

Nhân tiện, các câu lệnh trong 'if (parcelableArray! = Null) {...}' có thể được đơn giản hóa thành 'resultArray = Arrays.copyOf (parcelableArray, parcelableArray.length, MyClass []. Class);' – CrimsonX

34

ERROR/AndroidRuntime (5880): Gây ra bởi: java.lang.ClassCastException: [Landroid.os.Parcelable;

Theo phương thức API, phương thức readParcelableArray trả về mảng Parcelable (Parcelable []), không thể đơn giản được truyền vào mảng MyClass (MyClass []).

Nhưng bây giờ tôi nhận được ngoại lệ con trỏ Null.

Thật khó để nói nguyên nhân chính xác mà không có dấu vết ngăn xếp ngoại lệ chi tiết.


Giả sử bạn đã thực hiện MyClass thực hiện Parcelable đúng, đây là cách chúng ta thường làm cho serialize/deserialize một mảng các đối tượng parcelable:

public class ClassABC implements Parcelable { 

    private List<MyClass> mObjList; // MyClass should implement Parcelable properly 

    // ==================== Parcelable ==================== 
    public int describeContents() { 
    return 0; 
    } 

    public void writeToParcel(Parcel out, int flags) { 
    out.writeList(mObjList); 
    } 

    private ClassABC(Parcel in) { 
    mObjList = new ArrayList<MyClass>(); 
    in.readList(mObjList, getClass().getClassLoader()); 
    } 

    public static final Parcelable.Creator<ClassABC> CREATOR = new Parcelable.Creator<ClassABC>() { 
    public ClassABC createFromParcel(Parcel in) { 
     return new ClassABC(in); 
    } 
    public ClassABC[] newArray(int size) { 
     return new ClassABC[size]; 
    } 
    }; 

} 

Hope this helps.

Bạn cũng có thể sử dụng các phương pháp sau:

public void writeToParcel(Parcel out, int flags) { 
     out.writeTypedList(mObjList); 
    } 

    private ClassABC(Parcel in) { 
     mObjList = new ArrayList<ClassABC>(); 
     in.readTypedList(mObjList, ClassABC.CREATOR); 
    } 
+5

Không nên là 'MyClass.class.getClassLoader()' thay vì 'getClass(). GetClassLoader()'? Bởi vì tôi nghĩ rằng trình nạp lớp đang được sử dụng để tìm lớp Parceleable mà chúng tôi đang cố gắng đọc. –

+0

chính xác tôi không muốn ClassABC, tôi muốn MyClass – Srneczek

9

Tôi gặp sự cố tương tự và đã giải quyết theo cách này. tôi xác định một phương pháp helper trong MyClass, để chuyển đổi một loạt các Parcelable tới một mảng của các đối tượng MyClass:

public static MyClass[] toMyObjects(Parcelable[] parcelables) { 
    MyClass[] objects = new MyClass[parcelables.length]; 
    System.arraycopy(parcelables, 0, objects, 0, parcelables.length); 
    return objects; 
} 

Bất cứ khi nào tôi cần phải đọc một mảng parcelable của các đối tượng MyClass, ví dụ từ một ý định:

MyClass[] objects = MyClass.toMyObjects(getIntent().getParcelableArrayExtra("objects")); 

EDIT: Dưới đây là một phiên bản cập nhật của chức năng tương tự, mà tôi đang sử dụng gần đây hơn, để tránh lập cảnh báo:

public static MyClass[] toMyObjects(Parcelable[] parcelables) { 
    if (parcelables == null) 
     return null; 
    return Arrays.copyOf(parcelables, parcelables.length, MyClass[].class); 
} 
+0

Tôi đoán tôi sẽ đánh dấu câu trả lời này ngay bây giờ. Được vài lần bây giờ mà tôi đã trở lại để sao chép mã ma thuật này: D – Sufian

+0

@Giorgio Barchiesi, Trong studio android một cảnh báo là nhận được hiển thị: nguồn là Parcelable và đích là MyClass, nhưng ứng dụng hoạt động như mong đợi. Làm sao ? – kaushik

+0

@kaushik, cảm ơn bạn đã bình luận của bạn, tôi đã chỉnh sửa câu trả lời của tôi, vui lòng xem phiên bản mới của hàm "toMyObject". –

2

Bạn cần phải viết mảng sử dụng phương pháp Parcel.writeTypedArray() và đọc nó trở lại với phương pháp Parcel.readTypedArray(), như vậy:

MyClass[] mObjArray; 

public void writeToParcel(Parcel out, int flags) { 
    out.writeInt(mObjArray.length); 
    out.writeTypedArray(mObjArray, flags); 
} 

protected MyClass(Parcel in) { 
    int size = in.readInt(); 
    mObjArray = new MyClass[size]; 
    in.readTypedArray(mObjArray, MyClass.CREATOR); 
} 

Đối với danh sách, bạn có thể làm fo llowing:

ArrayList<MyClass> mObjList; 

    public void writeToParcel(Parcel out, int flags) { 
     out.writeTypedList(mObjList); 
    } 

    protected MyClass(Parcel in) { 
     mObjList = new ArrayList<>(); //non-null reference is required 
     in.readTypedList(mObjList, MyClass.CREATOR); 
    }