# 读 Android 开发艺术探索 &2

关键词:IPC / 多进程 / 序列化 / Binder

IPC,Inter-Process Communication 的缩写,含义为进程间通信或者跨进程通信,两个进程之间进行数据交换的过程。说到 IPC 的使用场景,就要提到多进程,当采用了多进程的设计方法,那么应用中就必须妥善处理进程间通信的各种问题。

[ 首先必须知道的几点:]

  1. 进程:一般是指一个执行单元,在 PC 和移动设备中指一个程序或者一个应用。
  2. 线程:CPU 调度的最小单元,是一种有限的系统资源。
  3. 一个进程可以包含多个线程,包含与被包含的关系。
  4. 一个进程可只有一个线程,主线程,在 Android 中就是 UI 线程,只有在 UI 线程里才能对界面元素进行操作。
  5. ANR,Application Not Responding,应用无响应,防止ANR,把耗时的任务放在子线程中执行。
  6. Android 有自己的进程间通信方式:Binder / Socket
  7. 通过系统提供的 ContentProvider 去查询数据的时候也是一种进程间通信,只不过通信细节被系统屏蔽了。

1. Android 中的多进程模式 #

在 Android 中使用多进程的常用方法只有一种:给四大组件在 AndroidManifest 中指定 android:process 属性。示例:

<activity android:name="io.github.isayes.MainActivity"
    ... />
<activity android:name="io.github.isayes.SecondActivity"  
    android:process=":remote" />
<activity android:name="io.github.isayes.ThirdActivity"
    android:process="io.github.isayes.remote" />

[ 需要知道以下几点:]

  1. 启动 SecondActivity 的时候系统会为它创建一个单独的进程:“io.github.isayes:remote”;
  2. 启动 ThirdActivity 的时候系统为它创建一个单独的进程:“io.github.isayes.remote”;
  3. 入口的 MainActivity 运行在默认的进程中,默认的进程名为包名;
  4. 进程名以 “:” 开头说明进程属于当前应用的私有进程(其它应用的组件不可以与之跑在同一个进程中),不以之开头的进程是全局进程(其它应用通过 ShareUID 方式与之可以跑在同一个进程中);
  5. Android 系统会为每个应用分配一个唯一的 UID,UID 相同才能共享数据(data 目录、组件信息等私有数据);UID 相同并且签名相同才能跑在同一个进程中(还可以共享内存数据)。

Android 系统为每一个应用分配了一个独立的虚拟机,或者说为每一个进程都分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,导致在不同的虚拟机中访问同一个类的对象会产生多份副本。一般来说,使用多线程会导致以下几个需要注意的问题 ↓

  1. 静态成员和单例模式失效
  2. 线程同步机制失效
  3. SharedPreferences 的可靠性下降
  4. Application 会多次创建(运行在同一个进程中的组件是属于同一个虚拟机和同一个 Application 的,同理,运行在不同的进程中的组件是属于不同的两个虚拟机和不同的 Application )
  5. 在多进程模式中,不同的进程的组件会有独立的虚拟机,独立的Application 以及内存空间;

理解同一个应用间的多进程:相当于两个不同的应用采用了 ShareUID 的模式。
为了解决多进程带来的问题,系统提供了很多跨进程通信的方法,虽然不能直接的共享内存,但是通过跨进程通信可以实现数据交互。实现跨进程通信的方式有:
1. 通过 Intent 传递数据
2. 共享文件
3. SharedPreferences
4. 基于 Binder 的 Messenger 和 AIDL
5. Socket 等

要理解 IPC 的各种方式,需要熟悉一些基础概念:序列化相关的 Serializable / Parcelable 接口 和 Binder

2. IPC 基础 相关概念 #

IPC 的基础概念有三点:
Serializable
Parcelable
Binder

Serializable 和 Parcelable 接口完成对象的序列化过程,当我们通过 Intent 和 Binder 传输数据时就需要使用 Parcelable 或者 Serializable。

若需要把对象持久化到存储设备上或者通过网络传输给其他客户端,这时候也需要使用 Serializable 来完成对象的持久化。

【关于 Serializable 需要知道的几点】

  1. Java 提供的序列化接口,是一个空接口
  2. 为对象提供标准的序列化和反序列化操作
  3. 几乎所有的工作都被系统自动完成了
  4. 静态成员变量属于类不属于对象,所以不会参与序列化过程
  5. 采用 transient 关键字标记的成员变量不参与序列化过程
  6. private static final long serialVersionUID = -1589804003600796026L;
    serialVersionUID 辅助序列化和反序列化过程,序列化后的数据的 serialVersionUID 要和 当前类的 serialVersionUID 相等才能被正常地反序列化

【关于 Parcelable 需要知道的几点】

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

[ 对 Parcelable 的典型用法示例 ]

public class User implements Parcelable{
    public int userId;
    public String userName;
    public boolean isMale;
    public Book book;

    public User(int userId, String userName, boolean isMale){
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }

    // 内容描述功能
    public int describeContents(){
        return 0;
    }

    // 序列化功能的完成,最终通过 Pacel 中的一系列 write 方法来完成
    public void writeToParcel(Parcel out, int flags){
        out.wirteInt(userId);
        out.writeString(userName);
        out.writeInt(isMale ? 1 : 0);
        out.writeOarcelable(book, 0);
    }

    // 反序列化功能的完成
    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
        public User createFromParcel(Parcel in) {
            return new User(in);
        }
        public User[] newArray(int size){
            return new User[size];
        }
    };

    private User(Parcel in){
        userId = in.readInt();
        userName = in.readString();
        isMale = in.readInt() == 1;
        book = in.readParcelable(Thread.currentThread().getContextClass-Loader());
    }
}

[ 对两种序列化接口的比较:]

Serializable / Parcelable
Serializable 是 Java 提供的序列化接口 / Parcelable 是 Android 提供的序列化接口,更适合在 Android 平台上
Serializable 使用简单但是开销大,序列化和反序列化过程中需要大量的 IO 操作 / Parcelable 使用麻烦但是效率很高

【Binder】

需要知道的几点 ↓

  1. Binder 是 Android 的一个实现了 IBinder 接口的类。
  2. 是 Android 的一种跨进程通信方式
  3. 是一种虚拟的物理设备,设备驱动是 /dev/binder
  4. 从 Android Framework 角度来看,Binder 是 ServiceManager 连接各种 Manager(ActivityManager、WindowManager 等等)和相应的 ManagerService 的桥梁。
  5. 从 Android 应用层来说,是客户端和服务端进行通信的媒介
  6. Binder 主要用在 Service 中,包括 AIDL、Messenger(底层也是 AIDL)
  7. 所有可以在 Binder 中传输的接口都需要继承 IInterface 接口

关于 Binder 的知识需要单独总结一下:读 Android 开发艺术探索 &3 (番外篇之弄懂 Binder)

End.

Note by HF.
Learn from 《Android 开发艺术探索》


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值