一、IPC介绍
IPC,是进程之间通信或跨进程通信,为Inter-Process Communication得缩写,IPC也不是Android所独有得,任何一个操作系统都需要有相应的IPC机制。
IPC应用的场景,既然是进程间通信,那就一定是多进程的场景下才考虑,比如某些工作需要运行在独立的进程中,或者利用多进程,开辟更大的内存空间等。Android中最有特色的进程间通信方式是Binder,除了Binder,还有Socket,管道、信号量、文件共享。
二、Android中的多进程模式
1.开启多进程模式
Android中多进程,一般是单个应用中存在多个进程的情况,当然也存在多个应用的情况,本文记录单进程方式。
正常情况下,需要给四大组件(Activity、Ser、Receiver、Content Provider),在Androidmenifest中指定android:process属性,除此,别无他法,除非以非正常方式,通过JNI在native层,fork一个进程。所以我们无法给一个线程或者一个实体类,指定其运行是的新进程。如下:
上面三个activity,将SencondActivity和Third Activity 指定不同的process属性,当启动mainactivity时,启动一个进程,当启动Second Activity时,启动"com.ryg.chaper_2:remote"进程,当Third Activity启动时,启动"com.ryg.chapter_2.remote",当然,mainActivity还是运行在默认进程,进程名是包名。注意,以":"开头的进程,是当前私有进程,其他应用的组件无法和它跑在一起,此外为全局进程,其他应用可以通过ShareUID方式和它跑在一起。当然也有限制条件,必须是Share UID相同,并且,签名也相同才可以,这种情况下,还可以访问彼此的私有数据,例如data目录,组件信息等。
2.多进程会造成的问题
- 静态变量和单例模式,完全失效
- 线程同步机制完全失效
- SharedPreferences的可靠性下降
- Application会多次创建
问题1.假如在上面例子中,加一个类,并添加静态变量,我们在Second Activity中修改,在ThirdActivity中查看,会发现,静态变量依旧没变化。因为每个进程,都有单独的虚拟机,而jvm都会分配单独的内存,所以,类和静态变量,都是独立的。所有后面,我们再学习,进程的数据共享。
问题2.本质上和第一个问题一样,不是同一块内存,当然锁对象还是锁全局,都不能保证线程同步。
问题3.SharedPreferences不支持多京城同时去执行写操作,应为它本身是通过读写xml文件实现的,显然并发会导致读写失败。
问题4.由于系统创建新进程的同时,会分配一个独立的虚拟机,所以这个过程也是重新启动一个应用的过程,当然应用也会启动一遍,那么,application 也会启动一次,可以通过上面例子,在application的oncreate打印进程名称来验证。
二、IPC的基础概念
基础概念主要包括三方面内容:Serializable接口、Parcelable接口以及Binder,其中Serializable和Parcelable接口可以完成对象的序列化过程,当我们通过Intent和Binder传输数据时,就要用到Serializable或者Parcelable。还有可能我们需要对象,持久化到设备中,或者通过网络传输给其他客户端,这事就需要Serializable完成对象的持久化。
1.Serializable接口
Serializable是Java 提供的一个序列化接口,在Android中是Parcel able接口,后面介绍。可以看到它是一个空接口,序列化和反序列化如下:
这里有个serializableUid,它到底是什么含义,serializableUid类似与一个索引,没有serializableUid,序列化可以实现,但反序列化,会失败。原则上,序列化后的数据中,serializableUid和当前类的serializableUid相同,才能被反序列化,还有当前类变量成员、类型发生变化,也是无法正常反序列化的,会报异常。
2.Parcelable接口
源码
序列化和反序列化
一个类只要实现Parcelable这个接口,就可以实现序列化和反序列化,并可以通过Intent和Binder传递,典型用法如下:
@SuppressLint("ParcelCreator")
class User implements Parcelable {
private String userName;
private String userAddress;
private Info info;
public User(String name, String address) {
this.userName = name;
this.userAddress = address;
}
@Override
public int describeContents() {
return 0;
}
//序列化
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(userName);
out.writeString(userAddress);
out.writeParcelable((Parcelable) info,0);
}
//反序列化
public static final Parcelable.Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
//Parcelde.read方法实现反序列化
public User(Parcel in){
userName = in.readString();
userAddress = in.readString();
info = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
}
Parcel内部封装了可序列化的数据,可以在Binder中传输。序列化用writeToParcel方法实现,最终由Parcel内部的一系列write方法实现。反序列化由CREATOR来完成,其内部标明了如何创建序列化对象和数组并通过Parcel的read方法实现。注意,实体类需要User(Parcel in)方法中,info是一个可序列化对象,所以需要传递当前线程的上下文才可以,不然会报类找不到的错误。Parcelable相关方法描述如下:
Serialazeble和parcelable的区别和优缺点
- Serialazeble是Java中的序列化接口,使用简单,但开销大,由大量的IO操作
- Parcelable是Android中的序列化方法,使用比较麻烦,但使用效率高,Parcelable是序列化到存储设备中或者将对象序列化后通过网络传输。
3、Binder
看到书中这句话,让我倒吸一口凉气!!!,继续吧,从三个方面了解
- IPC角度来说,Binder是Android中的一中跨进程通信方式的一个类,集成IBinder接口,也可以理解为一种,以/dev/binder为驱动的虚拟物理设备;
- Android FrameWork角度来说,Binder是Servicemanager连接Manager(ActivityManager、WindowManager…)和相应ManagerService的桥梁;
- Android应用层来说,Binder是服务端和客户端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个对象,客户端可以从服务端获取数据或服务端提供的服务,包括普通服务和基于AIDL的服务。
Android 开发中,Binder主要用在service中,包括AIDL和Messager,其中普通的Service中的Binder不涉及进程间通信,而Messager的底层是通过AIDL,所以我们用AIDL示例来学习Binder工作过程。关于AIDL的相关使用可以移步到Android进程通信-AIDL,相关内容,后续慢慢更新。
特别声明:内容总结来源《Android开发艺术探索》,仅记录学习,如有侵权或不对之处,还请告知,定当删除或改正