android ipc通信机制之二序列化接口和Binder
IPC的一些基本概念,Serializable接口,Parcelable接口,以及Binder。此核心为最后的IBookManager.java类!!!
Serializable接口,Parcelable接口都是可以完成对象的序列化过程。
序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
两者均可以实现序列化并且都可以用于Intent数据传递,那么如何的选取:
Serializable:是Java的序列化接口,使用简单但是开销很大,序列化反序列化过程需要大量I/O操作。
Parcelable:是Android的序列化接口,更适用于android平台,使用麻烦但是效率高,因此首选Parcelable。
Parcelable主要用于内存序列化上。若为对象序列化刀存储设备或者将对象序列化后进行网络传输最好使用Serializable,较为简单方便。
Paecelable接口:
1 import android.os.Parcel; 2 import android.os.Parcelable; 3 4 public class Book implements Parcelable { 5 int bookid; 6 String bookname; 7 public Book(int bookid,String bookname){ 8 this.bookid=bookid; 9 this.bookname=bookname; 10 } 11 //内容描述功能,几乎所有情况都返回“0”,仅当当前对象中存在文件描述符。 12 //文件描述符在形式上是一个非负整数。实际上,它是一个索引值, 13 // 指向内核为每一个进程所维护的该进程打开文件的记录表。 14 @Override 15 public int describeContents() { 16 return 0; 17 } 18 //序列化功能 writeToParcel,通过Parcel一系列write方法完成。 19 @Override 20 public void writeToParcel(Parcel dest, int flags) { 21 dest.writeInt(bookid); 22 dest.writeString(bookname); 23 } 24 //反序列化共嗯那个CREATOR,通过Pancel一系列read方法完成。 25 public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>(){ 26 27 @Override 28 public Book createFromParcel(Parcel source) { 29 return new Book(source); 30 } 31 32 @Override 33 public Book[] newArray(int size) { 34 return new Book[size]; 35 } 36 }; 37 private Book(Parcel source){ 38 bookid = source.readInt(); 39 bookname = source.readString(); 40 } 41 }
在Paecelable序列化的过程中需要实现序列化、反序列化、内容描述。
- 序列化功能 writeToParcel,通过Parcel一系列write方法完成。
-
反序列化通过CREATOR,通过Pancel一系列read方法完成。 public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>()
-
内容描述功能,几乎所有情况都返回“0”,仅当当前对象中存在文件描述符。
-
文件描述符:在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。
Binder:
从android应用层来说,Binder是客户端和服务端进行通讯的媒介,当使用bindSerivice时服务端会返回一个Binder对象,通过这个对象可以获取服务端的服务和数据。
Android开发中,Binder主要用在Service中,包括AIDL和Messenger,普通的Service中的Binder不涉及进程通信,而Messenger的核心是AIDL,现在新建一个AIDL实例。
AIDL:Android Interface Definition Language,即Android接口定义语言。
创建Book.java,Book.aidl,IBookManager.adil
Book.java:如上述代码。
Book.aidl:在android stuido 可以直接new aidl文件,其作用是Book类在AIDL的声明,(在IBokkManager.aidl文件用到了Book类,就需要声明Book.aidl)。其代码如下:
// Book.aidl package hang.myapplication; // Declare any non-default types here with import statements parcelable Book;
IBookManager.adil:是定义的一个接口,包含getBooklist和addBook方法。
注意:尽管Book类和IBookManager在同一包里,但是依然要导入Book类,这是AIDL的特殊之处。与此同时,系统会自动生成IBookManager.java类
之所以使用AIDL,是为了方便系统帮我们生成代码,也可以不借助AIDL自己书写代码,下面有介绍每个方法。。
// IBook.aidl package hang.myapplication; // Declare any non-default types here with import statements import hang.myapplication.Book; interface IBookManager { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ List<Book> getBookList(); void addBook(in Book book); }
IBook即为IBookManager,在笔者实例中,此处类名与上述名字有差异。
现在,我们就根据这个java类(系统生成的Binder类)分析Binder的工作原理。
IBookeManager.java:
此处为系统生成代码,较为混乱,可以看文字叙述。核心实现为,内部类stub和其内部代理类proxy。
/* * This file is auto-generated. DO NOT MODIFY. * Original file: C:\\Users\\hang\\Documents\\AndroidWorkSpace\\MyApplication\\MyApplication\\IPC\\src\\main\\aidl\\hang\\myapplication\\IBook.aidl */ package hang.myapplication; public interface IBook extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements hang.myapplication.IBook { private static final java.lang.String DESCRIPTOR = "hang.myapplication.IBook"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an hang.myapplication.IBook interface, * generating a proxy if needed. */ public static hang.myapplication.IBook asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof hang.myapplication.IBook))) { return ((hang.myapplication.IBook)iin); } return new hang.myapplication.IBook.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<hang.myapplication.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); hang.myapplication.Book _arg0; if ((0!=data.readInt())) { _arg0 = hang.myapplication.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements hang.myapplication.IBook { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public java.util.List<hang.myapplication.Book> getBookList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<hang.myapplication.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(hang.myapplication.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(hang.myapplication.Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((book!=null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public java.util.List<hang.myapplication.Book> getBookList() throws android.os.RemoteException; public void addBook(hang.myapplication.Book book) throws android.os.RemoteException; }
关键方法:
getBooklist、addBook:这是在IBookManager.aidl中声明的方法,同时在 IBookeManager.java 中为其声明了两个整型的id,分别标识这两个方法。
内部类Stub:其就是Binder类,当客户端和服务端在同一进程时,方法调用不会走进跨进程的transact过程。而位于不同进程时,则会执行该方法。这个逻辑
由Stub的内部代理Proxy来完成。
详细介绍:
- asInterface:用于将客户端的Binder对象转换成客户端所需要的AIDL接口类型对象,如果客户端和服务端位于同一进程,返回服务端的Stub对象本身。否则,
返回Stub.proxy对象。
DESCRIPTOR:Binder的唯一标识,一般是当前Binder的类名。
public static hang.myapplication.IBook asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof hang.myapplication.IBook))) { return ((hang.myapplication.IBook)iin); } return new hang.myapplication.IBook.Stub.Proxy(obj); }
- asBinder:返回当前Binder对象。
- boolean onTransact: 全方法为,onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
int code:可以确定客户端的目标方法是什么?可以看到switch case结构。
android.os.Parcel data:从这里取出目标方法的所需参数(如果目标方法有参数的话)。然后执行目标方法。
android.os.Parcel reply:目标方法执行完成后,向reply写入返回值(如果目标方法有返回值)。
以上亦为执行步骤~
目标方法:此案例getBooklist、addBook就是其中的目标方法。
若此方法返回false,那么客户端的请求会失败,可利用这一特性做权限验证。
- Proxy#getBooklist和Proxy#addBook:~~~~~~~
若是自定义,记得写上这些方法。
下一章,Android的各种跨进程通讯方式。