Android进程间通信(一):基础介绍

2022-12-19 13:45:56 浏览数 (3)

转载请以链接形式标明出处: 本文出自:103style的博客

《Android开发艺术探索》 学习记录


目录

  • 进程间通信简介
  • Android中的多进程模式
    • 如何开启多进程
    • 多进程模式的运行机制
  • 进程间通信基础概念介绍
    • Serializable接口
    • Parcelable接口
  • 小结

进程间通信简介

进程间通信IPC机制IPC 全称为 Inter-Process Communication

首先我们先了解下什么是进程,什么是线程?

  • 进程: 一般指一个执行单元。PC的一个程序 或者 移动设备的一个应用.
  • 线程:CPU调用的最小单元,是一种有限的系统资源。

一个进程可以包含多个线程。

IPC 不是 Android 独有的,任何一个操作系统都需要相应的 IPC 机制。比如:

  • Windows上可以通过 剪切板、管道和邮槽来进行进程间通信。
  • Linux 上可以 命令管道、共享内存、信号量等来进行进程间通信。

Android 是一种基于 Linux内核 的移动操作系统,它的进程间通信并不能完全继承 Linux,它有自己的进程间通信方式,比如:BinderSocket.


Android中的多进程模式

这里我们主要介绍两个问题:

  • 如何开启开启多进程模式呢 ?
  • 多进程的运行机制是怎样的呢 ?
如何开启开启多进程模式

那就是在 AndroidMainfest 文件中给 四大组件(ActivityServiceReceiverContentProvider) 指定 android:process

当然 我们也可以通过 JNInative层fork 一个进程。

示例:

代码语言:javascript复制
<activity
    android:name=".SecondActivity"
    android:process=":second" />
<service
    android:name=".TestService"
    android:process="com.lxk.test.testService" />
<receiver
    android:name=".TestReceiver"
    android:process=":testService" />
<provider
    android:name=".TestContentProvider"
    android:authorities="lxk"
    android:process=":testContentProvider" />

:xxx写法 会在前面添加当前的应用包名, 并且该进程为当前应用的私有进程。 不用:xxx写法的 TestService 则属于全局进程,其他应用可以通过 shareUID 方式和它跑在一个进程中。

运行程序,我们可以通过以下 shell 命令来查看.com.lxk.test 为包名

代码语言:javascript复制
adb shell ps | grep com.lxk.test

多进程的运行机制

《Android开发艺术探索》的作者是这样形容多进程的——“当应用开启了多进程以后,各种奇怪的现象都出现了”。

原因是 Android为每一个进程都分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这会导致一个类的对象会在每一个上都有一个副本。

所以运行在不同进程中的四大组件,只要它们之间通过内存来共享数据,都会共享失败。

一般来说,使用多进程会出现以下问题:

  • 静态成员和单例失效.
  • 线程同步机制完全失效.
  • SharedPreferences的可靠性降低.
  • Application 会多次创建.

进程间通信基础概念介绍

这里我们是对 Serializable接口Parcelable接口Binder 的介绍。

Serializable接口

Serializable是 Java 提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。

使用示例:

代码语言:javascript复制
public class User implements Serializable {
    public static final long serialVersionUID = 4516876541857684L;
    public int userId;
    public String name;
    public int age;
}

我们只需要实现 Serializable 接口,其他的工作几乎都被系统自动完成了。

使用 ObjectOutputStreamObjectInputStream 也可以轻松实现对象的序列化和反序列化。

代码语言:javascript复制
String url = "cache.txt";

User user = new User(1,"lxk",25);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(url));
oos.writeObject(user);
oos.close();

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(url));
User newUser = (User) ois.readObject();
ois.close();

我们注意到示例中有个 serialVersionUID,它是用来做什么的呢? serialVersionUID 一般是 IDE 根据当前类的结构自动生成的它的 hash值,它并不是必须的。 它的作用主要是用来 区分序列化的内容对应的类的结构是否发生了变化,如果结构发生了变化,就会导致序列化失败,程序crash.

当我们不需要序列化某些字段的时候,我们可以用 transient 字段来修饰.

代码语言:javascript复制
public class User implements Serializable {
    public static final long serialVersionUID = 4516876541857684L;
    public int userId;
    public String name;
    public int age;
    public transient int temp;
}

Parcelable接口

ParcelableAndroid 提供的序列化方式。 Parcelable 也是一个接口,我们只要实现这个接口,然后根据AndroidStudio的提示重写对应方法。

代码语言:javascript复制
public class User implements Parcelable {
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    public int userId;
    public String name;
    public int age;

    protected User(Parcel in) {
        userId = in.readInt();
        name = in.readString();
        age = in.readInt();
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(userId);
        dest.writeString(name);
        dest.writeInt(age);
    }
    @Override
    public int describeContents() {
        return 0;
    }
}

这里先说下,Parcel 内部包装了可序列化的数据,可以在 Binder 中自由传输。

通过以上示例代码,我们知道 序列化是由 writeToParcelParcel 的一系列 write 方法完成的,反序列化是由 CREATORParcel 的一系列 read 方法完成的.

Parcelable的方法说明:

方法

功能

标记位

createFromParcel(Parcel in)

从序列化后的对象中创建原始对象.

newArray(int size)

创建指定长度的原始数据对象数组.

User(Parcel in)

从序列化后的对象中创建原始对象.

writeToParcel(Parcel dest, int flags)

将当前对象写入序列化结构中. flags包含右侧标记为的值. 1 表示 正在写入的对象是一个返回值,一些实现可能在此时释放资源. 2 表示父对象将负责管理名义上跨其内部数据成员复制的重复状态/数据. 几乎所有情况都是 0.

PARCELABLE_WRITE_RETURN_VALUE = 0x0001 PARCELABLE_ELIDE_DUPLICATES = 0x0002

describeContents()

返回当前对象的内容描述. 1 表示包括文件描述符. 几乎所有情况都是 0.

CONTENTS_FILE_DESCRIPTOR = 0x0001

系统为我们提供了许多实现了 Parcelable 接口的类,它们都是可以直接序列化的。比如: IntentBitmapBundle等,同时 ListMap也可以序列化。


Serializable 和 Parcelable对比 Serializable 是Java提供的序列化接口,使用起来简单但是开销大,需要大量的 I/O操作Parcelable 是 Android提供的序列化接口,适合在Android平台,缺点就是使用起来相对麻烦点,但是效率高。

内存序列化 选 Parcelable. 序列化到存储设备 或者 序列化之后进行网络传输 则选 Serializable.


小结

  • 介绍了通过在 AndroidMainfest 文件中给 四大组件指定 android:process即可指定不同的进程。
  • 介绍了 SerializableParcelable 接口的使用和优缺点,内存序列化Parcelable,否则选 Serializable

下一节 通过 AIDL 介绍 Binder 的工作机制

如果觉得不错的话,请帮忙点个赞呗。

以上


0 人点赞