IPC是Inter-Process Communication的缩写,含义是跨进程通信,今天先来了解下IPC的基础知识。
一.多进程概念
1.进程&线程
进程是资源分配的最小单位,在PC和移动设备上指一个程序和应用,而线程是CPU调度的最小单位,是一种有限的系统资源,一个进程可以包含多个线程,线程分为主线程(UI线程)和子线程(做耗时操作),不可在主线程中做大量耗时操作,会导致ANR。
2.多进程的开启方式
(1)通过JNI在native层fork一个新的进程,这个不常用。
(2)在AndroidMenifest中给四大组件设置属性android:process,这个比较常用,这里来了解下process属性的命名规则。
a.默认进程,没有指定该属性则运行在默认进程,默认进程名为包名。
b.私有进程,以":“开头的进程,省略了包名,如android:process=”:remote",其完整的名称为com.example.myapplication:remote,私有进程表示其它进程的组件不能和它跑在同一个进程当中。
c.全局进程,完整命名的进程,如android.process=“com.example.myapplication.remote”,它表示其它应用可以通过shareUID的方式和它跑在同一个进程当中。
UID和shareUID:
Android系统为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。
两个应用通过shareUID跑在同一进程中的条件:shareUID相同且签名相同。满足这两个条件的应用无论是否跑在同一个进程中,都可以共享data目录、组件信息。若两个应用跑在同一个进程中,还可以共享内存数据。
3.多进程的使用场景
(1)某个应用由于自身原因需要多进程方式来实现,如某些模块由于特殊原因需要运行在独立进程。
(2)为了加大一个应用可使用的内存,通过多进程来获取多份内存空间。
(3)当前应用需要向其它应用获取数据。
4.多进程可能遇到的问题
(1)静态变量和单例模式失效,这是由于独立的虚拟机造成的。
(2)线程同步机制完全失效,这是由于独立的虚拟机造成的。
(3)SharedPreference可靠性降低,SharedPreference不支持两个进程并发读写操作,会有一定几率导致数据丢失。
(4)Application多次创建,android系统会为新进程分配独立的虚拟机,相当于系统把应用重新启动了一次。
二.序列化
1.序列化介绍
(1)含义:序列化表示把一个对象转换成可存储或可传输的状态,序列化后的对象可以在网络上进行传输,也可以存储到本地。
(2)使用场景:需要通过Intent或Binder传输类对象就必须完成对象的序列化过程。
(3)方式:通过Serializable和Parcelable接口。
2.Serializable和Parcelable的比较
注意:两种变量不会参与序列化过程,一个是静态成员变量属于类,而不属于对象,另外一个是被transient关键字标记的成员变量。
3.具体实现
(1)实现Serializable接口
public class Person implements Serializable {
private static final long serialVersionUID = 162468432145313L;//辅助序列化和反序列化,原则上序列化后数据的中serialVersionUID要和当前类的serialVersionUID相同才能正常的序列化
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
(2)实现Parcelable接口
public class Person implements Parcelable {
private String name;
private int age;
protected Person(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator CREATOR = new Creator() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
三.Binder机制
1.进程架构
每个Android的进程都运行在独立的虚拟空间,虚拟空间又分为用户空间和内核空间,对于用户空间是不能共享的,内核空间则可以共享,Client端和Service端进行进程通信,利用的就是可共享的内核内存空间来完成底层的通信工作,其原理图如下:
(1)从API角度,是一个类,实现IBinder接口。
(2)从IPC角度,是Android中的一种跨进程通信方式。
(3)从Framework角度,是ServiceManager连接各个Manager和相应的ManagerService的桥梁。
(4)从应用层角度,是客户端和服务端进行通信的媒介。
3.优点
(1)性能方面
Binder机制传输效率高、可操作性强,其传输效率主要影响因素是内存拷贝次数,拷贝次数越少,传输效率越高,对于消息队列、socket和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到接收方缓存区,一共要拷贝两次。而对于Binder来说,数据从发送方的缓存区拷贝到内核缓存区,而接收方的缓存区与内核缓存区是映射到同一块物理地址的,节省了一次数据拷贝的过程。
(2)安全方面
Binder机制安全性能高,传统Linux IPC接收方无法获得对方进程可靠的PID/UID,从而没法鉴别对方身份,而Binder机制为每个进程分配了PID/UID且在Binder通信时会根据PID/UID进行有效性检测。
4.通信模型
Binder基于C/S的结构下,定义了四个角色:Server、Client、ServerManager、Binder驱动。其中前三者是在用户空间的,彼此之间无法直接进行交互,Binder驱动是属于内核空间的,负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持,其原理图如下:
(1)从Server进程来看,Binder是存在的实体对象,client通过transact()函数,经过Binder驱动,最终回调到Binder实体的onTransact()函数中。
(2)从Client进程的角度看,Binder指的是对Binder代理对象,是Binder实体对象的一个远程代理,通过Binder驱动进行交互。
6.代码实现
(1)定义一个接口,继承IInterface,代表服务端提供给客户端的能力。
public interface PersonManager extends IInterface {
void addPerson(Person person);
List getPersonList();
}
(2)定义Server中Binder实体对象,继承Binder,实现PersonManager。
public abstract class IPersonManager extends Binder implements PersonManager {
//唯一标识
public static final String DESCRIPTOR = "com.czy.binderdemo.ipc";
public static final int TRANSAVTION_addPerson = IBinder.FIRST_CALL_TRANSACTION;
public static final int TRANSAVTION_getPerson = IBinder.FIRST_CALL_TRANSACTION+1;
//Binder驱动传递过来的IBinder对象
public static PersonManager asInterface(IBinder iBinder) {
//查找本地binder对象,如果有就返回,没有就返回代理
IInterface iInterface = iBinder.queryLocalInterface(DESCRIPTOR);
if(null!=iInterface && iInterface instanceof PersonManager){
return (PersonManager) iInterface;
}
return new Proxy(iBinder);
}
//返回当前Binder对象
@Override
public IBinder asBinder() {
return this;
}
//调用服务端不同的方法
//code用于区分执行哪个方法
//data客户端传递过来的参数
//reply服务器返回回去的值
//flags标明是否有返回值,0为有(双向),1为没有(单向
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION://获取当前接口的描述符
reply.writeString(DESCRIPTOR);
return true;
case TRANSAVTION_addPerson://执行addPerson方法
data.enforceInterface(DESCRIPTOR);
Person parcel= null;
if (data.readInt() != 0) {
parcel = Person.CREATOR.createFromParcel(data);
}
this.addPerson(parcel);
reply.writeNoException();
return true;
case TRANSAVTION_getPerson://执行getPerson方法
data.enforceInterface(DESCRIPTOR);
List result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
}
return super.onTransact(code, data, reply, flags);
}
//不在同一个进程时返回的代理
public static class Proxy implements PersonManager{
//远程代理Binder
private IBinder mIBinder;
public Proxy(IBinder binder) {
this.mIBinder = binder;
}
@Override
public void addPerson(Person mPerson) {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (mPerson != null) {
data.writeInt(1);
mPerson.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
//客户端会通过Binder驱动的transact()方法调用服务端代码
mIBinder.transact(IPersonManager.TRANSAVTION_addPerson, data, replay, 0);
replay.readException();
} catch (RemoteException e){
e.printStackTrace();
} finally {
replay.recycle();
data.recycle();
}
}
@Override
public List getPersonList() {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
List result = null;
try {
data.writeInterfaceToken(DESCRIPTOR);
mIBinder.transact(IPersonManager.TRANSAVTION_getPerson, data, replay, 0);
replay.readException();
result = replay.createTypedArrayList(Person.CREATOR);
}catch (RemoteException e){
e.printStackTrace();
} finally{
replay.recycle();
data.recycle();
}
return result;
}
@Override
public IBinder asBinder() {
return null;
}
}
}
(3)定义Service
public class PersonService extends Service {
private List mPeople = new ArrayList<>();
private String TAG = "PersonService";
@Override
public void onCreate() {
super.onCreate();
mPeople.add(new Person("张三",12));
mPeople.add(new Person("李四",15));
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return iPersonManager;
}
public IPersonManager iPersonManager = new IPersonManager() {
@Override
public void addPerson(Person person) {
if (person != null) {
mPeople.add(person);
}
Log.d(TAG,"Person 数量 : "+mPeople.size());
}
@Override
public List getPersonList() {
return mPeople;
}
};
}
(4)客户端绑定服务,获取Binder对象,调用方法进行交互。
Intent intent = new Intent(this, PersonService.class);
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG,"connect success");
personManager = IPersonManager.asInterface(service);
List personList = personManager.getPersonList();
Log.d(TAG,"Person 数量 :"+personList.size());
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG,"connect failed");
}
};
public void click(View view) {
if (personManager != null) {
personManager.addPerson(new Person("王二麻子",14));
}
}