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接口后,也是通过通过I/O操作来序列化。
他两的区别就是Parcelable针对安卓优化过,传输更有效率
实现方法:
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 CREATOR = new Creator() {
@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,注意本文目的是理解aidl是怎样通过aidl传输消息的。不是讲解如何写aidl通信。
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 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 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 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 getBookList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
List 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
2、data中写入binder标识DESCRIPTOR
3、调用transact(调用后,客户端线程挂起,等待调用到服务端onTransact,并执行完毕后返回)
4、reply取出返回结果readException
5、reply取出返回数据
Proxy#addBook
和Proxy#getBookList基本一样,只是没有第五步,因为没有返回值
到此,Binder的使用,调用流程就很明朗了。
初步理解Binder后,建议阅读PackageManagerService服务框架详解对AIDL深一步理解。