什么是IPC
IPC是跨进程通信的简称,全称太长了,不想写了。由于Android中分为了UI线程和其他线程,而且对UI的操作只能在UI线程,为了防止卡顿,我们需要把一些操作放到其他线程处理。那么处理完毕之后如何通知UI线程做出处理呢?这就要靠IPC机制了。
当然,IPC不止在Android中有运用,很多地方也有使用。
Android中的多进程模式
开启多进程模式很简单,只需要在menifest文件中添加android:process属性即可,具体情况网上有很多例子,不再赘述。
但是开启了多进程之后,很多单进程时的问题就浮现了出来,比如,数据共享的时候同步会出问题,作者对这些情况做了总结,大致有如下几种
1.静态成员和单例模式失效
2.线程同步机制失效
3.SharedPreferences的可靠性下降
4.Application会多次创建
Android中的序列化
在进入IPC的总结之前,先来总结一个必不可少的东西,序列化,我们熟悉的是继承Serializable接口,来实现对象的可序列化过程,这种方式很简单,基本不用做多余操作,最多设置一下ID以反序列化之后的比较之用。
Android也提供了一套序列化机制——Parcelable接口,要实现这个接口就需要复杂一些的方法,需要实现几个必备的方法,也很简单,不详细说明。
两种序列化机制的区别
那么,这两种机制有何不同呢?首先,Serializable接口的序列化方式是java提供的,开销比较大,但是胜在简单。
而Parcelable的序列化方式开销比较小,但是使用起来稍显复杂,在应用内部的情况下,应该优先选择使用。
如果是在网络传输的过程中,建议使用Serializable方式,首先是简单,其次是不会因为Android系统的版本而造成差异。
Binder概括
首先,Binder是Android中的一个类,实现了IBinder接口,是链接各种manager和managerservice的桥梁,是客户端与服务端通信的桥梁。
那么,Binder到底是如何运作的呢?我们来看一个书上的例子,一个系统生成的AIDL文件的结构
public class BookManagerImpl extends Binder implements IBookManager {
/** Construct the stub at attach it to the interface. */
public BookManagerImpl() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an IBookManager interface, generating a proxy
* if needed.
*/
public static IBookManager asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return ((IBookManager) iin);
}
return new BookManagerImpl.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
public 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 ((0 != data.readInt())) {
arg0 = Book.CREATOR.createFromParcel(data);
} else {
arg0 = null;
}
this.addBook(arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
@Override
public List<Book> getBookList() throws RemoteException {
// TODO 待实现
return null;
}
@Override
public void addBook(Book book) throws RemoteException {
// TODO 待实现
}
private static class Proxy implements IBookManager {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.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 {
reply.recycle();
data.recycle();
}
}
}
}
这个例子是一个手动实现的Binder,整个文件看起来非常的长而无须,我们不妨跟随作者的脚步一点点来分析它
第一部分,这是一个叫BookManagerImpl的类,继承了Binder并实现了IBookManager接口
第二部分,其内部有一个内部类,private static class Proxy implements IBookManager同样也实现了IBookManager接口
BookManagerImpl的各个方法
1.public static IBookManager asInterface(IBinder obj) 用于转化服务端的Binder对象为客户端的AIDL接口,区分进程,如果是跨进程,返回的是proxy对象,否则的话返回stud对象本身
2.public IBinder asBinder() 返回当前的Binder对象
3.public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 运行在Binder线程池中,根据code和参数来执行不同的方法,参数从data中获取,然后把结果写入reply中,如果返回false,客户端的请求会失败。
4.public List<Book> getBookList() 与 public void addBook(Book book) throws RemoteException 实现了IBookManager接口中的方法
内部类Proxy的各个方法
大致与BookManagerImpl相同,不过内部的getBook和addBook是通过RPC来实现的,这些方法都运行在客户端,当进行RPC时会被挂起,所以不能在主线程操作,当RPC结束时,会读取结果并写入。
到了这里,Binder的工作流程就大概介绍完了,是不是看起来很复杂,仔细分析起来却很简单呢,有了Binder,我们就可以继续分析接下来的形形色色的IPC方式了。
Android中的几种IPC机制概览
首先说一下Android中的几种常见的IPC机制,Binder是android自己独特的一套机制,在系统层级经常被用到,很多IPC方式都会用到它,上面已经做过了介绍。
android中IPC的方式大概有以下几种
1.使用Bundle 最常见的IPC方式,在多个场合都被使用
2.文件共享 通过对一个文件的读写来进行通信的方式
3.使用Messager 相信大家都很熟悉,messager和handler的组合使用来进行IPC
4.使用AIDL android为我们提供的一种Binder的简化使用方式,实现容易
5.ContentProvider 是Android中专门用于对外部提供数据访问的方式,也是唯一方式,例如读取联系人
6.使用Socket 把跨进程通信当做网络通信来处理,也可以实现IPC的效果
由于1,2,6的方式很简单也很常见,这里就不详细描述了,下面来重点总结一下3,4,5的Android中独特的IPC方式
Messager方式的IPC
顾名思义,使用Message对象来进行IPC,其底层是一个AIDL,这是对其的封装,每次只会处理一条Messager消息,所以不存在并发的问题
服务端
创建一个Service,同时创建一个Handler并通过它创建一个Messager对象,并在onbind将其的binder返回即可
客户端
使用bindService绑定服务,并在ServiceConnection对象的哦你ServiceConnected方法中把返回的IBinder对象转化为Messager对象,在建立Message对象,加入数据,通过Messager对象的send方法进行发送
如果要服务端接收到消息后有回复的话,通过msg的replyTo来获取一个Messager对象,然后和客户端发送的过程是一样的
而客户端,需要一个接受消息的Messager和Handler,还需要在发消息给服务端的时候把服务端接收回复的Messager通过Message的replyTo发回去,这样服务端才能拿到它,进行收到消息之后的回复。
AIDL方式的IPC
服务端
首先定义AIDL文件,其实就是一些特殊格式的接口,不过参数有所限制,需要是可序列化对象或者几个限定的类型之一
其次,实现远程服务端的Service,主要是实现系统生成的XXXManager.stub()内部类中的AIDL接口中对应的方法
其他杂项
RemoteCallbackList类,可以实现跨进程删除添加listener,对于做观察者模式开发有帮助,不过使用方式比较特殊,在使用前必须beginBroadcast使用后需要finishBroadcast。
耗时方法不要在UI线程调用
还需要注意断开连接之后的重连
以及访问的权限认证,可以在onbind方法或者ontransact方法中进行。
客户端
使用bindService绑定服务,并设定绑定成功后的ServicConnection类,将返回的Service转换为AIDL类型,然后就可以调用其中的方法了·
ContentProvider方式的IPC
该方式底层依然是使用的Binder,不过经过系统的封装,更加简单易用
创建FfR
使用非常简单,新建一个类,继承ContentProvider类并实现其必须实现的几个方法即可,具体的内部实现可以自己定义
在menifest文件中申明为provider,并添加android:authorities属性,android:permission属性即可
使用
通过getContentResolver.methodname(params)来调用即可,当然,如果需要对应权限,要申请,methodname对应于几个已经实现的方法,参数表中比较重要的一个是uri,对应android:authorities的值
不同IPC方式的比较
Bundle 简单易用,但是只能传输支持的数据类型,可以用于四大组件之间传输数据
文件共享 简单易用,但是不适合高并发,适用无并发,实时性不高的场景
AIDL 功能强大,支持一对多并发,有些复杂,需要处理好线程同步,用于一对多有远程过程调用的情况
Messager 功能一般,但是不能处理高并发,不支持RPC,只能传输Bundle支持的类型,用于底并发情况,无RPC情况
ContentProvider 数据源访问方面强大,支持一对多并发,可以实现自定义操作,主要提供CRUD操作,用于一对多的进程间数据共享
Socket 功能强大,支持一对多实时通信,不支持直接RPC,细节繁琐,用于网络数据交换场景