AIDL 传递对象集合 所遇到的问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/EthanCo/article/details/82530566

在项目使用AIDL中,发现自己以前对于AIDL的理解有些误区。
以前使用AIDL增加方法,只要iadl文件里的方法顺序不变,在所有的方法之后,增加新方法,新的AIDL是可以兼容原先的AIDL的。即用新的AIDL也可以和老的AIDL进行通讯 (只要不调用新增加的方法就不会有问题)。
但是,在传递LIst<Bean>的过程中,发现,如果Bean的字段有增减,则新Aidl和老Aidl通讯会有问题,无法兼容。此时,Client端和Server端的Bean必须是同一个,不能有差别。

以下是做的相关的实验:

RemoteSong.java

public class RemoteSong implements Parcelable {
    private int id;
    private String title;
    private int duration;
    //get / set 省略

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.id);
        dest.writeString(this.title);
        dest.writeInt(this.duration);
    }

    public RemoteSong() {
    }

    protected RemoteSong(Parcel in) {
        readFromParcel(in);
    }

    public void readFromParcel(Parcel in) {
        this.id = in.readInt();
        this.title = in.readString();
        this.duration = in.readInt();
    }

    public static final Creator<RemoteSong> CREATOR = new Creator<RemoteSong>() {
        @Override
        public RemoteSong createFromParcel(Parcel source) {
            return new RemoteSong(source);
        }

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

RemotePlayList.java

public class RemotePlayList implements Parcelable {
    private int id;
    private String name;
    private List<RemoteSong> songs = new ArrayList<>();
    //get / set 省略

    public List<RemoteSong> getSongs() {
        if (songs == null) {
            return new ArrayList<>();
        }
        return songs;
    }

    public void setSongs(List<RemoteSong> songs) {
        this.songs = songs;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.id);
        dest.writeString(this.name);
        dest.writeTypedList(this.songs);
    }

    public RemotePlayList() {
    }

    protected RemotePlayList(Parcel in) {
        readFromParcel(in);
    }

    public void readFromParcel(Parcel in) {
        this.id = in.readInt();
        this.name = in.readString();
        this.songs = in.createTypedArrayList(RemoteSong.CREATOR);
    }

    public static final Creator<RemotePlayList> CREATOR = new Creator<RemotePlayList>() {
        @Override
        public RemotePlayList createFromParcel(Parcel source) {
            return new RemotePlayList(source);
        }

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

IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.heiko.aidltest;

import com.heiko.aidltest.bean.Person;
import com.heiko.aidltest.bean.RemotePlayList;

interface IMyAidlInterface {
    RemotePlayList getLocalList();
}  

此时,在Aidl Client端程序中,调用getLocalList()获取的数据是正确的

    [{
        "duration": 1000,
        "id": 0,
        "title": "标题0"
    }, {
        "duration": 1001,
        "id": 1,
        "title": "标题1"
    }, {
        "duration": 1002,
        "id": 2,
        "title": "标题2"
    }, {
        "duration": 1003,
        "id": 3,
        "title": "标题3"
    }, {
        "duration": 1004,
        "id": 4,
        "title": "标题4"
    }]

接着,我们把 Aidl Server端程序的RemoteSong中增加一个fileName字段

public class RemoteSong implements Parcelable {
    private int id;
    private String title;
    private int duration;
    private String fileName;
    //get / set 省略

    public RemoteSong() {
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.id);
        dest.writeString(this.title);
        dest.writeInt(this.duration);
        dest.writeString(this.fileName);
    }

    protected RemoteSong(Parcel in) {
        readFromParcel(in);
    }

    private void readFromParcel(Parcel in) {
        this.id = in.readInt();
        this.title = in.readString();
        this.duration = in.readInt();
        this.fileName = in.readString();
    }

    public static final Creator<RemoteSong> CREATOR = new Creator<RemoteSong>() {
        @Override
        public RemoteSong createFromParcel(Parcel source) {
            return new RemoteSong(source);
        }

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

此时,可以发现,再运行Server端和Client端程序,会发现,获取的数据不正确。

[{
    "duration": 1000,
    "id": 0,
    "title": "标题0"
}, {
    "duration": 0,
    "id": 1324770695
}, {
    "duration": 1001,
    "id": 1,
    "title": "标题1"
}, {
    "duration": 0,
    "id": 1324770695
}, {
    "duration": 1002,
    "id": 2,
    "title": "标题2"
}]

为什么数据会出现偏差

先附上IMyAidlInterface.java

@Override
public com.heiko.aidltest.bean.RemotePlayList getLocalList() throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    com.heiko.aidltest.bean.RemotePlayList _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_getLocalList, _data, _reply, 0);
        _reply.readException();
        if ((0 != _reply.readInt())) {
            _result = com.heiko.aidltest.bean.RemotePlayList.CREATOR.createFromParcel(_reply);
        } else {
            _result = null;
        }
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}  

查看AIDL生成的IMyAidlInterface.java文件及RemotePlayList里的Parcelable 读写规则,可以发现,是通过Parcelable来进行读写的

public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(this.id);
    dest.writeString(this.name);
    dest.writeTypedList(this.songs);
}

public void readFromParcel(Parcel in) {
    this.id = in.readInt();
    this.name = in.readString();
    this.songs = in.createTypedArrayList(RemoteSong.CREATOR);
}  

进入writeTypedList方法,我们来看下

public final <T extends Parcelable> void writeTypedList(List<T> val) {
        if (val == null) {
            writeInt(-1);
            return;
        }
        int N = val.size();
        int i=0;
        writeInt(N);
        while (i < N) {
            T item = val.get(i);
            if (item != null) {
                writeInt(1);
                item.writeToParcel(this, 0);
            } else {
                writeInt(0);
            }
            i++;
        }
    }

可以看到,List会遍历,逐个调用RemoteSong的writeToParcel进行存储,我们知道,上面Demo中,Client端的RemoteSong只写入了4个字段

扫描二维码关注公众号,回复: 3339870 查看本文章
private void readFromParcel(Parcel in) {
    this.id = in.readInt();
    this.title = in.readString();
    this.duration = in.readInt();
    this.fileName = in.readString();
}

而Server端的RemoteSong却只读取了3个字段

public void readFromParcel(Parcel in) {
    this.id = in.readInt();
    this.title = in.readString();
    this.duration = in.readInt();
}

所以,多个遍历读取的时候,就会出现混乱,数据读取会出现错误。

public final <T> ArrayList<T> createTypedArrayList(Parcelable.Creator<T> c) {
    int N = readInt();
    if (N < 0) {
        return null;
    }
    ArrayList<T> l = new ArrayList<T>(N);
    while (N > 0) {
        if (readInt() != 0) {
            l.add(c.createFromParcel(this));
        } else {
            l.add(null);
        }
        N--;
    }
    return l;
}  

小结

所以,我们在使用AIDL传递对象集合的时候,尽量不要修改Bean对象,如果要修改,要确保Server端和Client端的Bean对象保持统一。

相关Demo程序下载

猜你喜欢

转载自blog.csdn.net/EthanCo/article/details/82530566