文章目录

        • 一、Android中的多进程模式
          • 1. 多进程的情况
          • 2. 开启多进程模式
          • 3. 多进程模式的运行机制
        • 二、IPC基础概念
          • 1. Serializable接口
          • 2. Parcelable接口
          • 3. Serializable 和 Parcelable 区别
          • 4. Binder
        • 三、Android中的IPC方式
          • 1. 使用Bundle
          • 2. 使用文件共享
          • 3. 使用Messager
          • 4. 使用AIDL
          • 5. 使用ContentProvider
          • 6. 使用Socket
          • 7. 六种方式对比
        • 四、Binder连接池

IPC 是 Inter-Process Communication 的缩写,含义为进程间通信或跨进程通信。任何一个操作系统都有IPC机制,对于Android来说,Binder是其特色的进程间通信方式。

一、Android中的多进程模式

1. 多进程的情况
  • 一个应用因为某些原因自身需要采用多进程模式来实现,比如某些模块可能需要运行在单独的进程中,或者需要通过多进程加大内存
  • 当前应用需要向其他应用获取数据
2. 开启多进程模式

在Android中,多进程只有一种方法,就是给自大组件在AndroidManifest中指定 android:process 属性(通过JNI在native层去fork一个新的进程暂不考虑)

假设包名为com.test

  • 第一种方式 “:” ,进程属于当前应用私有进程,其他应用的组件不可以和它跑在同一个进程中,最终进程名为"come.test:remote",要在当前进程名前面附加上当前包名
  • 第二种方式属于全局进程,是完整的进程名,其他应用通过ShareUID可以和它跑在同一个进程中
  • 没有指定此属性的在默认进程中,进程名为包名
3. 多进程模式的运行机制

Android为每一个应用分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,所有运行在不同进程中的四大组件,只要它们之间通过内存来共享数据,都会共享失败

使用多进程会造成:

  • 静态成员和单例模式完全失效
  • 线程同步机制完全失效 (不同进程锁的不是同一个对象)
  • SharedPreference 的可靠性下降 (SP不支持两个进程同时执行写操作,会导致一定几率的数据丢失,其底层是读写XML文件,并发写有问题)
  • Application 会多次创建 (不同进程的组件拥有独立的虚拟机、Application以及内存空间,运行在同一个进程中的组件属于同一个虚拟机和同一个Application,运行在不同进程中的组件属于两个不同的虚拟机和Application)

二、IPC基础概念

1. Serializable接口

Serializable是Java提供的一个序列化接口,是一个空接口,通过Serializable实现对象序列化,只需要实现Serializable接口并声明一个serialVersionUID(可选)

serialVersionUID作用

序列化的时候系统会把当前类的serialVersionUID写入序列化文件(或其他中介),反序列化时系统会去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换,无法正常反序列化

如果不手动指定serialVersionUID值,系统会根据自动计算当前类的hash值并赋给serialVersionUID,这样当类有所改变时(比如删除了某个成员变量),其serialVersionUID也会被系统自动重新计算赋值,导致反序列化失败;手动指定serialVersionUID的值,可以在当前类发生某些变化后,仍最大限度恢复数据;注意,如果类的结构发生了改变(比如改变类名),尽管serialVersionUID验证通过,反序列化还是会失败

不参与序列化的值

  • 静态成员变量属于类不属于对象,不参与序列化过程
  • 用transient关键字标记的成员变量不参与序列化过程

demo

一个实现了序列化的类

public class User implements Serializable{    private static final long serialVersionUID = 123456789043215647L;    public int userId;    public String userName;    public boolean isMale;    public User(int userId, String userName, boolean isMale) {        this.userId = userId;        this.userName = userName;        this.isMale = isMale;    }}

序列化与反序列化

private void serialize() {        User user = new User(0,"jake",true);        try {            ObjectOutputStream out = new ObjectOutputStream(                    new FileOutputStream(context.getFilesDir().getPath().toString() + "/serialize.txt"));            out.writeObject(user);            out.close();        } catch (IOException e) {            e.printStackTrace();        }    }private void deserialize() {        try {            ObjectInputStream in = new ObjectInputStream(                    new FileInputStream(context.getFilesDir().getPath().toString() + "/serialize.txt"));            User newUser = (User)in.readObject();            Log.e("wyc",newUser.toString());            in.close();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }

注:从本地存储中恢复的newUser和user的内容完全一样,但两者并不是同一个对象

2. Parcelable接口

系统已经为我们提供了许多实现了Parcelable接口的类,比如Intent、Bundle、Bitmap等,同时List和Map也可以序列化,前提是它们里面的每个元素都是可序列化的

自定时实现:

public class UserParcel implements Parcelable {    public int userId;    public String userName;    public boolean isMale;    public Book book;    public UserParcel(int userId, String userName, boolean isMale) {        this.userId = userId;        this.userName = userName;        this.isMale = isMale;    }    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel parcel, int i) {        parcel.writeInt(userId);        parcel.writeString(userName);        parcel.writeInt(isMale ? 1 : 0);        parcel.writeParcelable(book, 0);    }    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {        @Override        public UserParcel createFromParcel(Parcel parcel) {            return new UserParcel(parcel);        }        @Override        public UserParcel[] newArray(int i) {            return new UserParcel[i];        }    };    private UserParcel(Parcel parcel) {        userId = parcel.readInt();        userName = parcel.readString();        isMale = parcel.readInt() == 1;        book = parcel.readParcelable(Thread.currentThread().getContextClassLoader());    }}

Parcel 内部包装了可序列化的数据,可以再Binder中自由传输。

Parcelable实现序列化:

  • writeToParcel:实现序列化
  • CREATOR:反序列化,内部标明了创建序列化对象和数组
  • describeContents:内容描述,默认返回0,仅当对象中存在文件描述符,返回1

注意:User中book是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器

3. Serializable 和 Parcelable 区别
  • Serializable是Java中的序列化接口,使用起来开销很大,序列化和反序列化都需要大量的I/O操作。

  • Parcelable是Android中的序列化方法,更适用于Android平台,缺点是使用稍麻烦,但是效率很高,Android推荐。

  • Parcelable主要用于内存序列化,通过Parcelable将对象序列化到存储设备或者进行网络传输会比较复杂,这两种情况建议使用Serializable。

4. Binder

Binder是Android中的一个类,实现了IBinder接口;从IPC角度,Binder是Android中的一种跨进程通信方式,BInder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder;从 Android Framework 角度来说,Binder是ServiceManager 连接各种Manager和相关ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介。Binder主要用在Service中,包括AIDL和Messager。

AIDL Android接口定义语言

作用:方便系统为我们生成代码从而实现跨进程通讯

以一个AIDL(Android接口定义语言)demo分析Binder的工作机制(也可以自己写,使用AIDL主要是为了方便系统为我们生成代码):

完整代码参见:AIDL-DEMO

1)新建一个Book.java类 实现了Parcelable接口,是一个表示图书信息的类

package com.wyc.cpt2.aidl;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable {    public int bookId;    public String bookName;    public Book(int bookId, String bookName) {        this.bookId = bookId;        this.bookName = bookName;    }    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel parcel, int i) {        parcel.writeInt(bookId);        parcel.writeString(bookName);    }    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {        @Override        public Book createFromParcel(Parcel parcel) {            return new Book(parcel);        }        @Override        public Book[] newArray(int i) {            return new Book[i];        }    };    private Book(Parcel parcel) {        bookId = parcel.readInt();        bookName = parcel.readString();    }        @Override    public String toString() {        return String.format("[bookId:%s, bookName:%s]", bookId, bookName);    }}

2)新建Book.aidl,是Book类在AIDL中的声明

// Book.aidlpackage com.wyc.cpt2.aidl;parcelable Book;

3)新建IBookManager.aidl 是我们定义的一个接口,里面有两个方法,getBookList用于从远程服务端获取图书列表,addBook用于往图书列表中添加一本书

// IBookManager.aidlpackage com.wyc.cpt2.aidl;import com.wyc.cpt2.aidl.Book;interface IBookManager {    List getBookList();    void addBook(in Book book);}

注意:尽管Book类和IBookManager在同一个包,在IBookManager中仍要导入Book类

再一个,aidl包是和java包平级的,在src->main下,其他位置无效,这个右键生成AIDL文件,自动就会生成这个文件夹并将生成的.aidl文件放在其下相应目录中了

第2章 IPC机制_第1张图片

然后学习系统为IBookManager生成的Binder类,在如图位置:

第2章 IPC机制_第2张图片

先大概了解一下Binder机制流程:

第2章 IPC机制_第3张图片

看下一这个类:

第2章 IPC机制_第4张图片

  • 这个类继承了IInterface接口,同时自己也是一个接口,所有可以再Binder中传输的接口都需要继承IInterface;

  • DESCRIPTOR 是Binder的唯一标识

  • 声明了一个内部类Stub,这个是一个Binder类

第2章 IPC机制_第5张图片

  • 声明了两个方法 getBookList 和 addBook,同时声明了两个 id用于标识这两个方法

第2章 IPC机制_第6张图片

  • asInterface:用于将服务端的Binder对象转换成客户端需要的AIDL接口类型的对象,如果客户端和服务端位于同一个进程,那么返回服务端service对象本身,否则返回系统封装后的Stub.proxy代理对象
  • asBinder:返回当前的Binder对象

第2章 IPC机制_第7张图片

  • onTransact:这个方法运行在服务端中的BInder线程池中,客户端发起跨进程请求,远程请求会通过系统底层封装后交由此方法处理。首先通过code确定请求目标,接着从data中取出参数,然后执行

第2章 IPC机制_第8张图片

  • Proxy内部类#getBookList#addBook:这两个方法运行在客户端,当客户端调用此方法时,首先创建输入型对象 _data,输出型对象 _reply,返回值对象 _result,接着把参数写入 _data,再调transact发起RPC(远程过程调用)请求,同时当前线程挂起,服务端的onTransact会被调用,直到RPC过程返回,当前线程继续执行,并从 _reply 中取出返回结果

注意

  • 客户端发起远程请求时,当前线程会被挂起直至服务端进程返回数据,所以远程方法如果是耗时的,不能再UI进程中发起请求
  • 服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方法去实现(这个还不太理解)

三、Android中的IPC方式

1. 使用Bundle

Activity、Srevice、Receiver都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以它可以方便的在不同进程间传输

注意Bundle中传输的数据必须能够被序列化,比如基本类型、实现了Parcelable或Serializable接口的对象以及一些Android支持的特殊对象

2. 使用文件共享

两个进程通过读写同一个文件来交换数据,对文件格式没有要求,注意并发操作

SharePreference也是文件的一种,底层上采用XML文件存储键值对,但是系统对它的读写操作有一定的缓存策略,即内存中会有缓存,因此多进程模式下读写就不可靠

3. 使用Messager

Messager是一种轻量级的IPC方案,底层实现是AIDL

在不同进程中传递Message对象,在Message中放入我们要传递的数据

一次只处理一个请求,服务端不用考虑线程同步问题

实现Messager:

1)服务端进程创建一个Service来处理客户端请求,同时创建一个Handler对象,并通过它来创建一个Messager对象,然后再Service的onBind中返回这个Messager对象底层的Binder

2)客户端进程绑定服务端Service,用服务端返回的IBinder对象创建Messager,通过这个Messager就可以向服务端发消息了,消息类型为Message对象

3)如果需要服务端回应,客户端也需要创建一个Handler和Messager,并把这个Messager通过Message的replyTo参数传递给服务器

4)服务器通过这个replyTo参数就可以回应客户端

注意

Message中能使用的载体只有what、arg1、arg2、Bundle以及replyTo,object对象2.2以前不支持跨进程传输,2.2以后也仅仅是系统提供的实现了Parcelable接口的对象才能通过它来传输

4. 使用AIDL

1)服务端创建一个Service和一个AIDL接口,AIDL中声明暴露给客户端的接口,Service中shixian这个接口

2)客户端绑定服务,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了

AIDL支持的数据类型:

  • 基本数据类型
  • String 和 CharSequence
  • List:ArrayList且里面的每个元素都被AIDL支持
  • Map:HashMap且里面的每个元素都被AIDL支持
  • Parcelable接口的实现对象
  • AIDL 接口本身

注意:

  • 自定义Parcelable对象和AIDL对象必须显示import
  • AIDL中用到的自定义Parcelable对象,必须新建一个同名的.aidl文件
  • AIDL中除了基本数据类型,其他类型必须标注方向:in/out/inout
  • AIDL的包结构在服务端和客户端要保持一致

demo同Binder机制分析时的demo,还有RemoteCallbackList未添加,使用AIDL做权限验证未学习

5. 使用ContentProvider

ContentProvider底层实现同样也是Binder

自定义ContentProvider只需要继承ContentProvider类并实现六个方法:onCreate、query、update、insert、delete和getType;其中onCreate由系统回调并运行在主线程,其他五个方法由外界回调并运行在Binder线程池中

ContentProvider对底层的数据存储方式没有任何要求,可以使用SQLite数据库,也可以使用普通的文件等

要观察一个ContentProvider中的数据改变情况,可通过ContentProvider的registerContentObserver方法来注册观察者,通过unregisterContentObserver方法来解除观察者

6. 使用Socket

Socket不仅能实现进程间通信,还可以实现设备间通信

7. 六种方式对比
名称 优点 缺点 适用场景
Bundle 简单易用 只能传输Bundle支持的数据类型 四大组件间的进程通信
文件共享 简单易用 不适合高并发场景,并且无法做到进程间的即时通信 无并发访问庆幸,交换简单的数据实时性不高的场景
AIDL 功能强大,支持一对多并发通信,支持实时通信 使用较复杂,需要处理好线程同步 一对多通信且有RPC需求
Messenger 功能一般,支持一对多串行通信,支持实时通信 不能很好处理高并发庆幸,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型 低并发的一对多即时通信,无RPC需求,或者无须要返回结果的RPC需求
ContentProvider 在数据源访问方面功能强大,支持一对多并发数据共享,可通过call方法扩展其他操作 可以理解为受约束的AIDL,主要提供数据源的CRUD操作 一对多的进程间的数据共享
Socket 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 实现细节稍微有点繁琐,不支持直接的RPC 网络数据交换

四、Binder连接池

待学习

更多相关文章

  1. Android中使用AlarmManager需注意设置进程属性
  2. Android 抽象回调函数以及接口回调更新UI
  3. [Android]RecyclerView基本使用+adapter回调接口实现点击事件
  4. Android学习笔记之网络接口(Http接口,Apache接口,Android接口)
  5. Android 多进程学习
  6. Android中使用Gson解析JSON数据,以及把JSON数据映射成一个对象

随机推荐

  1. android中 关于listitem 获得焦点时,改变
  2. ionic 打包android@6出现xml/network_sec
  3. Android各层推荐开发书籍及参考资料!!!
  4. Android中的信使Messenger的源码解析
  5. Android(安卓)Arch LifeCycle用法详解与
  6. OnTouchListener与OnGestureListener的区
  7. android JSON解析数据 android解析天气预
  8. android 五大布局-线性布局(LinearLayout)
  9. Maven Android使用一
  10. Android(安卓)使用Pull方法解析XML文件的