Android IPC 机制

IPC 机制全面解析

Posted by Song on February 20, 2021

Android IPC 简介

  1. 进程间的通讯(Inter-Prosses communication),简称IPC
  2. 进程间通讯方式有很多,如Bundle、Socket、文件、管道、Messager、Provider、AIDL等
  3. 设置多进程方式,manifest 配置 android:process=":remote" 或者android:process="com.song.androidstudy.remote"
  4. :remote 方式进程名为包名 + :remote,否则为 com.song.androidstudy.remote
  5. :remote 开头的进程是当前应用的私有进程,其他进程的组件不可以跑在一起,但是非 : 开头的进程可以通过相同的 sharedUID 跑在同一个进程当中,为全局进程。
  6. 静态成员和单例对于完全失效,线程同步完全失效,Application 会创建多次,SharedPreferences 可靠性下降(虽然可以进程间通讯,但系统会缓存)

对象序列化

Serializable

  • 实现 Serializable 接口,且声明 serialVersionUID 即可实现序列化和反序列化,注意 serialVersionUID 并不是必须的
  • 序列化的时候系统会把当前类的 serialVersionUID 写入系列化的文件中,当反序列化的时候系统会去检测文件中的 serialVersionUID,看它时候和类中的 serialVersionUID 一致,如果一致就说明序列化类的版本和当前类的版本是相同的,这个时候可以成功反序列化。否则就说明当前类和序列化类发生了某些变换,可能报 InvalidClassException
  • 可以手动指定 serialVersionUID,也可以通过工具生成当前类的 hash 值作为 serialVersionUID。但并不是只要 serialVersionUID 相同反序列化就会成功,如果类发生了非常规性改变,如改了类名、改了属性名称反序列化就会失败。
  • 静态成员变量属于类不属于对象,不会参与序列化和过程;transient 关键字标记成员变量不参与序列化过程
  • 重写 writeObjectreadObject 方法可以自定义序列化过程

Parcelable

只要实现 Parcelable 接口,对象就可以实现序列化并可以通过 Intent 和 Binder 传递数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class User implements Parcelable {

    private int userId;
    private String userName;

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.userId);
        dest.writeString(this.userName);
    }

    public void readFromParcel(Parcel source) {
        this.userId = source.readInt();
        this.userName = source.readString();
    }

    protected User(Parcel in) {
        this.userId = in.readInt();
        this.userName = in.readString();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}

Parcelable 方法说明

方法 功能 标记位
createFromParcel(Parcel source) 从序列化后的对象中创建原始对象  
newArray(int size) 创建指定长度的原始对象数组  
User(Parcel in) 从序列化后的对象中创建原始对象  
writeToParcel(Parcel dest, int flags) 将当前对象写入序列化结构中,其中 flags 标识有两种:0和1(参见右侧标记位),为 1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都返回 0。 PARCELABLE_WRITE_RETURN_VALUE
describeContents() 返回当前对象的内容描述。如果含有文件描述符,返回1(参见右侧标记位),否则返回 0,几乎所有情况都返回 0。 CONTENTS_FILE_DESCRIPTOR

Intent、Bundle、Bitmap、List、Map 也可以序列化,前提是其里面每个元素都是可序列化的。

Parcelable 和 Serializable 区别

  • Serializable 是 Java 中序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量的 I/O 操作
  • Parcelable 使用稍微麻烦些,但是其效率高适合在 Android 中使用
  • 在内存中首选使用 Parcelable,但是在文件和网络中推荐使用 Serializable

    https://www.jianshu.com/p/32a2ec8f35ae ,Parcelable序列化速度比Serializable快10倍

选用合适的IPC方式

  1. bundle 利用 intent 传递数据,只支持序列化的数据,且数据量不宜过大
  2. 文件通讯,并发支持弱,因为 android 文件读写没有锁
  3. SharedPreferences,可靠性弱,且不支持并发
  4. Messager 底层实现为 AIDL,轻量级的 IPC 机制,一次只执行一次请求,不支持并发,通过 Message 传输数据
名称 优点 缺点 适用场景
Bundle 简单易用 只能传递Bundle支持的数据类型 四大组件间的进程间通信
文件共享 简单易用 不适合高并发场景, 并且无法做到进程间的即时通信 无并发访问情形, 交换简单的数据实时性不高的场景
AIDL 功能强大,支持一对多并发通信,支持实时通信 适用稍复杂,需要处理好线程同步 一对多通信且有RPC需求
Messenger 功能一般,支持一对多穿行通信,支持实时通信 不能很好处理高并发情形, 不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型 低并发的一对多即时通信,无RPC需求,或者无须要返回结果的RPC需求
ContentProvider 在数据源访问方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作 可以理解为受约束的AIDL,主要提供数据源的CRUD操作 一对多的进程间的数据共享
Socket 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 实现细节稍微有点繁琐,不支持直接的RPC 网络数据交换