Android Serializable、Parcelable和Binder的使用、理解

IPC for Android 系列:
1、IPC for Android
2、本文Serializable、Parcelable、Binder的使用、理解
3、IPC bindService传递Messenger
4、结合PMS源码讲解AIDLPackageManagerService服务框架详解

进程间通信的时候,把一个类的实例对象发送到另外一个进程的时候,就需要对这个类进行序列化和反序列化。实例对象序列化后就可以传输到另外一个进程中。再反序列化就可以得到一个一模一样类的实例对象了。
Serializable和Parcelable都是序列化接口,Binder则是进程间通信的一种工具。以下将详细说明。

Serializable

Serializable是java提供的序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化。
实现方法:
1、继承Serializable接口
2、声明serialVersionUID
demo:
声明一个book类继承Serializable接口,并声明serialVersionUID

public class Book implements Serializable {
    private static final long serialVersionUID =354615249875612456L;

    public String name;

    public Book (String name) {
        this.name = name;
    }

    public String getBookName() {
        return this.name;
    }
}

它的序列化和饭序列化操作也比较简单,只需要用到ObjectOutputStream和ObjectInputStream就可以了

try {
    Book aaa = new Book("aaa");
    ObjectOutputStream outputStream = new ObjectOutputStream(openFileOutput("book.txt", Context.MODE_APPEND));
    outputStream.writeObject(aaa);
    outputStream.close();
} catch (IOException e) {
    e.printStackTrace();
}

try {
    ObjectInputStream inputStream = new ObjectInputStream(openFileInput("book.txt"));
    Book otherAaa = (Book) inputStream.readObject();
    if(otherAaa != null) {
        Log.d("yink","book name = " + otherAaa.getBookName());
    } else {
        Log.d("yink","book is null");
    }
    inputStream.close();
} catch (Exception e) {
    e.printStackTrace();
}

运行结果

04-27 11:20:55.305 15328 15328 D yink    : book name = aaa

重点!!
反序列化得到的otherAaa这个实例对象,和aaa不是同一个,等于新创建的一个Book类。任何序列化反序列化都是这样。不能得到原有的类,只是新创建了一个一模一样的类的实例对象。

这里讲一下声明serialVersionUID的作用 :
在反序列化的时候,系统会对比serialVersionUID这个值,若相同则反序列化成功。
比如Book这个类,如果我不指定serialVersionUID,系统会给它一个默认值,假设系统给的serialVersionUID = 1L;那么序列化的时候serialVersionUID被保存起来,在book.txt保存的序列化文件里serialVersionUID被保存的值就是1L。
当我修改了Book这个类,比如增加了一个变量,系统会重新给serialVersionUID赋值,假设系统给的serialVersionUID = 2L; 这个时候Book类里面的serialVersionUID的值就是2L,当反序列化的时候,系统就会对比serialVersionUID,由于保存的serialVersionUID=1L,反序列化就不能成功了,所以需要制定serialVersionUID这个值

Parcelable

实现Serializable接口后,通过I/O操作完成了序列化和反序列化过程。
实现Parcelable接口后,则是可以在Intent和Binder中传输。
实现方法:
1、继承Parcelable接口
2、实现Parcelable中的方法
demo:

public class Book implements Parcelable {

    public String name;
    public int page;

    public Book (String name, int page) {
        this.name = name;
        this.page = page;
    }

    public String getBookName() {
        return this.name;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(page);
    }

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

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

    protected Book(Parcel in) {
        name = in.readString();
        page = in.readInt();
    }
}

Parcel:这个类内部包装了可序列化数据,可在binder中自由传输
主要实现三个方法:
1、writeToParcel,完成序列化,通过parcel的write完成
2、CREATOR,反序列化,其中createFroParcel创建一个新的反序列化对象book,并通过parcel的read方法,把数据赋给新的对象Book并返回(对象就反序列化了)。newArray作用为创建指定长度的原始对象数组
3、describeContents,内容描述功能,几乎所有情况都返回0,为1表示当前对象需要作为返回值返回,不能立即释放资源。

Android中有很多类已经实现了Parcelable接口,如Intent,Bundle,Bitmap等,List和Map也支持(需要里边每个元素可序列化)

两个接口比较:
Serializable,java提供的接口,使用简单开销大,需要大量I/O操作。适合用于将对象存储在设备中,或通过网络传输。
Parcelable,android平台提供并推荐使用方法,使用稍麻烦,效率高。主要用在内存的序列化上。

binder

手动实现一个Binder
1、声明一个AIDL性质接口;
2、实现代理类Proxy;

我们先声明一个IBookManager接口,AIDL性质接口只需继承IInterface。

public interface IBookManager extends IInterface{
    static final String DESCRIPTOR = "com.example.android.myapplication.IBookManager";
    static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0;
    static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1;
    public List<Book> getBookList() throws RemoteException;
    public void addBook(Book book) throws RemoteException;
}

有多少个方法就需要声明多少个id;
DESCRIPTOR,binder的唯一标识,一般用当前binder的类名表示,这里是在接口里定义了;

然后书写Proxy代理类:(也可省略第一步,直接继承IInterface,统一写到BookManagerImpl里)

public class BookManagerImpl extends Binder implements IBookManager {

    public BookManagerImpl() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static IBookManager asInterface(IBinder obj) {
        if(obj == null) {
            return  null;
        }
        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if ((iin != null) && (iin instanceof IBookManager)) {
            return (IBookManager)iin;
        }
        return new BookManagerImpl.Proxy(obj);
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;
            case TRANSACTION_getBookList:
                data.enforceInterface(DESCRIPTOR);
                List<Book> result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;
            case TRANSACTION_addBook:
                data.enforceInterface(DESCRIPTOR);
                Book arg0;
                if (data.readInt() != 0) {
                    arg0 = Book.CREATOR.createFromParcel(data);
                } else {
                    arg0 = null;
                }
                this.addBook(arg0);
                reply.writeNoException();
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    @Override
    public List<Book> getBookList() throws RemoteException {
        return null;
    }

    @Override
    public void addBook(Book book) throws RemoteException {

    }

    private static class Proxy implements IBookManager {
        private IBinder mRemote;

        Proxy(IBinder remote) {
            this.mRemote = remote;
        }

        @Override
        public IBinder asBinder() {
            return mRemote;
        }

        public  String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            List<Book> result;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(TRANSACTION_getBookList, data, reply, 0);
                reply.readException();
                result = reply.createTypedArrayList(Book.CREATOR);
            } finally {
                reply.recycle();
                data.recycle();
            }
            return result;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if(book != null) {
                    data.writeInt(1);
                    book.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_addBook, data, reply, 0);
                reply.readException();
            } finally {

            }
        }
    }
}

BookManagerImpl服务端代码,为客户端提供通信方法
分别说下各个方法功能:
asInterface
客户端调用此方法,区分进程,若客户端和服务端同一进程,返回IBookManager本身,若不是同一进程,返回代理类Proxy;
asBinder
返回当前binder对象,即BookManagerImpl
onTransact
此方法运行在服务端Binder线池中,当客户端请求,通过系统底层封装,调用到onTransact(code, data, reply, flags)此方法;
然后执行switch(code),通过之前定义的方法id,去定方法,data传递需要的数据,reply被写入返回值;
此方法若返回false,客户端调用会失败,可以此做一些权限验证等;
getBookList和addBook
函数里什么都没写,只是为了继承IBookManager而写,无实际用处;

Proxy
代理类,用来返回给客户端,这个类关键作用就是实现了客户端对服务端的调用
Proxy#getBookList
1、定义Pacel对象data、replay,List<Book>对象result
2、data中写入binder标识DESCRIPTOR
3、调用transact(调用后,客户端线程挂起,等待调用到服务端onTransact,并执行完毕后返回)
4、reply取出返回结果readException
5、reply取出返回数据
Proxy#addBook
和Proxy#getBookList基本一样,只是没有第五步,因为没有返回值

到此,Binder的使用,调用流程就很明朗了。
初步理解Binder后,建议阅读PackageManagerService服务框架详解对AIDL深一步理解。

    原文作者:Yink_Liu
    原文地址: https://www.jianshu.com/p/3a7ac774c493
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞