Messenger 传递 List对象以及遇到的问题

Messenger 传递 List对象

假设需要传递的List对象为:
List<MyData> list = new ArrayList<>();

类 MyData 的定义为:
public class MyData implements Parcelable {

    public int id; 
    public String name;
	
	 public MyData(int _id, String _name) {
	     this.id = _id;
		 this.name = _name;
	 }
	
	public static final Creator<MyData> CREATOR = new Creator<MyData>() {
        @Override
        public MyData createFromParcel(Parcel in)
        {
            return new MyData(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags)
    {

        dest.writeInt(id);
        dest.writeString(name);
       
    }

}

在服务端的Messenger中将List对象放到bundle中传递List对象
List<MyData> list = getMyDataList();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("list", list);
msgToClient.setData(bundle);
try {
	msgfromClient.replyTo.send(msgToClient);
} catch (RemoteException e){
	e.printStackTrace();
}

一切看起来没任何问题,所有步骤都是对的, MyData对象也序列化了。
但是这行会报错bundle.putParcelableArrayList("list", list);
说是list对象未序列化,查了下确实是,于是改为ArrayList。修改后如下:
ArrayList arrayList = new ArrayList<>();
List<MyData> list = getMyDataList();
arrayList.add(list);
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("list", arrayList);
msgToClient.setData(bundle);
try {
	msgfromClient.replyTo.send(msgToClient);
} catch (RemoteException e){
	e.printStackTrace();
}
修改后不报错了,然后写客户端代码。

在客户端接收该对象
Bundle data = msgFromServer.getData();
ArrayList list = data.getParcelableArrayList("list")
List<MyData> mList = (List<MyData>) list.get(0);
结果运行的时候直接crash,报错:
ClassNotFoundException when unmarshalling.....找不到com.xxx.xxx.MyData
报错的是这行:ArrayList list = data.getParcelableArrayList("list")
一脸懵逼。。。

百度了一翻后,在stackoverflow看到了一个感觉可行的方法:
Bundle data = msgFromServer.getData();
data.setClassLoader(MyData.class.getClassLoader());//加上这行
ArrayList list = data.getParcelableArrayList("list")
List<MyData> mList = (List<MyData>) list.get(0);
还真是可以了...

总结:
1. Bundle传递对象一定要序列化,Android推荐用Parcelable,不解释。
2. ArrayList可以直接传递,因为已经序列化,看源码就知道。
   ArrayList: public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
   而List没有:
   List: public interface List<E> extends Collection<E> 
   
3. 在Android上classloader是个蛋疼的东西,深究下去估计是个bug。查看Parcel.java源码,找到when unmarshalling关键字,调用的是readParcelableCreator这个函数,
   参数就是ClassLoader,而报ClassNotFoundException就是掉用Class<?> parcelableClass = Class.forName(name, false /* initialize */,parcelableClassLoader);导致的。
   这里的name应该是拿到了ArrayList,而不是MyData.所以需要指定。
   /** @hide */
    public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
        String name = readString();
        if (name == null) {
            return null;
        }
        Parcelable.Creator<?> creator;
        synchronized (mCreators) {
            HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader);
            if (map == null) {
                map = new HashMap<>();
                mCreators.put(loader, map);
            }
            creator = map.get(name);
            if (creator == null) {
                try {
                    // If loader == null, explicitly emulate Class.forName(String) "caller
                    // classloader" behavior.
                    ClassLoader parcelableClassLoader =
                            (loader == null ? getClass().getClassLoader() : loader);
                    // Avoid initializing the Parcelable class until we know it implements
                    // Parcelable and has the necessary CREATOR field. http://b/1171613.
                    Class<?> parcelableClass = Class.forName(name, false /* initialize */,
                            parcelableClassLoader);
                    if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
                        throw new BadParcelableException("Parcelable protocol requires that the "
                                + "class implements Parcelable");
                    }
                    Field f = parcelableClass.getField("CREATOR");
                    if ((f.getModifiers() & Modifier.STATIC) == 0) {
                        throw new BadParcelableException("Parcelable protocol requires "
                                + "the CREATOR object to be static on class " + name);
                    }
                    Class<?> creatorType = f.getType();
                    if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
                        // Fail before calling Field.get(), not after, to avoid initializing
                        // parcelableClass unnecessarily.
                        throw new BadParcelableException("Parcelable protocol requires a "
                                + "Parcelable.Creator object called "
                                + "CREATOR on class " + name);
                    }
                    creator = (Parcelable.Creator<?>) f.get(null);
                }
                catch (IllegalAccessException e) {
                    Log.e(TAG, "Illegal access when unmarshalling: " + name, e);
                    throw new BadParcelableException(
                            "IllegalAccessException when unmarshalling: " + name);
                }
                catch (ClassNotFoundException e) {
                    Log.e(TAG, "Class not found when unmarshalling: " + name, e);
                    throw new BadParcelableException(
                            "ClassNotFoundException when unmarshalling: " + name);
                }
                catch (NoSuchFieldException e) {
                    throw new BadParcelableException("Parcelable protocol requires a "
                            + "Parcelable.Creator object called "
                            + "CREATOR on class " + name);
                }
                if (creator == null) {
                    throw new BadParcelableException("Parcelable protocol requires a "
                            + "non-null Parcelable.Creator object called "
                            + "CREATOR on class " + name);
                }

                map.put(name, creator);
            }
        }

        return creator;
    }

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>