概述
IPC即进程间通信,是两个进程间数据交换的过程。AIDL即安卓接口定义语言,它能简化我们实现进程间通信的过程。
**进程和线程:**进程一般指一个执行单元,在PC和移动设备上指一个程序或一个应用;线程是CPU调度的最小单元,同时线程是一种有限的系统资源。
Android中的多进程模式
开启多进程:在AndroidMenifest中给activity指定android:process属性。
不同进程的组件会有独立的虚拟机,Application和内存空间。使用多进程会导致以下几个方面的问题:
1.静态成员和单例模式失效
2.线程同步机制失效
3.SharedPreferences可靠性下降
4.Application会多次创建
IPC基础概念
1.Serializable接口
使用方法:自定义类实现Serializable接口,在其中声明类似于 private static final long serialVersionUID = 666666L 的标识即可,如果不声明的话反序列化可能会失败。
**注意:**静态成员变量属于类不属于对象,所以不会参与序列化过程;用transient标记的成员变量不参与序列化过程。
2.Parcelable接口
只要实现Parcelable接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递,使用方法即实现Parcelable接口并重写需要的抽象方法。
**Serializable和Parcelable的优缺点:**前者使用简单,但开销很大;后者较为复杂,但效率很高。
再有,前者是Java中的序列化接口,后者是Android推荐的序列化方式。在将对象序列化到存储设备中或将对象序列化后通过网络传输两种情况推荐使用Serializable来简化操作,其他情况使用Parcelable的方式。
3.Binder及AIDL的使用
Binder是Android中的一个类,它实现了IBinder接口。Binder是Android中的一种跨进程通信方式,是客户端与服务端通信的媒介。Binder主要用在Service中,包括AIDL和Messager,而Messenger底层也是AIDL,所以书中用AIDL来分析Binder的工作机制。下面通过一个AIDL例子来慢慢分析Binder的工作过程。
先新建Book.java:
package com.example.aidldemo;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable { //实现Parcelable接口
public int bookId;
public String bookName;
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
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];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}
Book.aidl,是Book类在AIDL文件中的声明。AIDL文件在AS中的创建:右键->AIDL->AIDL File
package com.example.aidldemo;
parcelable Book;
IBookManager.aidl
package com.example.aidldemo;
import com.example.aidldemo.Book; //虽然Book类和IBookManager在同一个包下,但还需要导入
interface IBookManager {
List<Book> getBookList();
//其中in表示数据只能由客户端流向服务端,out表示数据只能由服务端流向客户端,而inout则表示数据可在服务端与客户端之间双向流通
//官方文档中说明:基本参数的定向tag默认是并且只能是in
void addBook(in Book book);
}
下面是IBookManger.aidl生成的Binder类:IBookManager.java,这个Binder类就是我们进程间通信实现的核心,我们根据它来分析Binder的工作原理。
注意:因为两个进程间进行通信是一个双向的相对的过程,哪个进程发送信息就成为了“客户端”进程,哪个进程接收另一个进程发送的信息就成了“服务端”进程,所以每个进程都需要发送和接收信息的代码,系统自然也就将这些代码写在了一个文件中。
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.aidldemo;
public interface IBookManager extends android.os.IInterface {
/**
* Default implementation for IBookManager.
*/
public static class Default implements com.example.aidldemo.IBookManager {
@Override
public java.util.List<com.example.aidldemo.Book> getBookList() throws android.os.RemoteException {
return null;
}
@Override
public void addBook(com.example.aidldemo.Book book) throws android.os.RemoteException {
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/**
* Local-side IPC implementation stub class. 本地段IPC的实现类stub
*/
public static abstract class Stub extends android.os.Binder implements com.example.aidldemo.IBookManager {
//Binder的唯一标识,一般用当前Binder的类名表示
private static final java.lang.String DESCRIPTOR = "com.example.aidldemo.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidldemo.IBookManager interface,generating a proxy if needed.
将服务端的Binder对象转换成客户端所需要的AIDL接口类型的对象,需要时(服务端和客户端不在同一进程时)生成一个proxy对象
*/
public static com.example.aidldemo.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//通过instanceof关键字判断两者是否在同一进程,若两者在同一进程,则返回的是服务端的Stub对象本身,不会调用跨进程的transact方法
if (((iin != null) && (iin instanceof com.example.aidldemo.IBookManager))) {
return ((com.example.aidldemo.IBookManager) iin);
}
//两者不在同一进程,返回系统封装好的Stub.Proxy代理对象,客户端调用会通过transact方法发起跨进程请求
return new com.example.aidldemo.IBookManager.Stub.Proxy(obj);
}
//此方法返回当前Binder对象
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
此方法运行在服务端的Binder线程池中
参数code:要调用的方法的标识码
data:客户端传过来的参数
reply:回传给客户端的数据
flags:为true时表示执行成功,否则失败
*/
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<com.example.aidldemo.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.example.aidldemo.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.aidldemo.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.aidldemo.IBookManager {
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;
}
/**
此方法在客户端的Binder线程池中运行
*/
@Override
public java.util.List<com.example.aidldemo.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<com.example.aidldemo.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//客户端发起远程请求,同时当前线程挂起,服务端的onTransact方法被调用
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.example.aidldemo.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.example.aidldemo.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);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
public static com.example.aidldemo.IBookManager sDefaultImpl;
}
//两个方法的常量标识
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.example.aidldemo.IBookManager impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.aidldemo.IBookManager getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public java.util.List<com.example.aidldemo.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.aidldemo.Book book) throws android.os.RemoteException;
}
下面是Binder工作机制图:
4.AIDL总结
如果没有AIDL,上面系统自动生成的代码就需要我们自己来实现,而这正好体现了AIDL存在的价值:简化我们的开发过程,让我们更方便地实现进程间通信。
参考资料
《安卓开发艺术探索》