android基础知识Android技术知识Android开发经验谈

要点提炼|开发艺术之IPC

2017-12-22  本文已影响185人  厘米姑娘

在上一篇Window里提及过IPC,本篇将详细总结IPC,知识点如下:


一、IPC基础及概念

1.多进程模式

a.进程&线程

两者关系: 一个进程可包含多个线程,即一个应用程序上可以同时执行多个任务。

  • 主线程(UI线程):UI操作
  • 有限个子线程:耗时操作

注意:不可在主线程做大量耗时操作,会导致ANR(应用无响应)。

b.开启多进程模式的方式

UID&ShareUID:

  • Android系统为每个应用分配一个唯一UID,具有相同UID的应用才能共享数据。
  • 两个应用通过ShareUID跑在同一进程的条件:ShareUID相同且签名也相同。
    • 满足上述条件的两个应用,无论是否跑在同一进程,它们可共享data目录,组件信息。
    • 若跑在同一进程,它们除了可共享data目录、组件信息,还可共享内存数据。它们就像是一个应用的两个部分。

c.查看进程信息的方法

d.需要进程间通信的必要性:所有运行在不同进程的四大组件,只要它们之间需要通过内存在共享数据,都会共享失败。

原因:由于Android为每个应用分配了独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这会导致在不同的虚拟机中访问同一个类的对象会产生多份副本。

e.多进程造成的影响,总结为以下四方面:

①静态变量和单例模式失效。

②线程同步机制失效。

③SharedPreference的不可靠下降。

④Application多次创建。

推荐阅读关于Android多进程


2.序列化

a.序列化的介绍

  • 含义:序列化表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。
  • 场景:需要通过Intent和Binder等传输类对象就必须完成对象的序列化过程。
  • 两种方式:实现Serializable/Parcelable接口。

b.Serializable接口和Parcelable接口的比较:

c.serialVersionUID

注意:两种变量不会参与序列化过程:

  • 静态成员变量属于类,不属于对象。
  • transient关键字标记的成员变量。

推荐阅读序列化Serializable和Parcelable的理解和区别


3.IPC简介

a.IPC(Inter-Process Communication,跨进程通信):指两个进程之间进行数据交换的过程。

b.任何一个操作系统都有对应的IPC机制。

c.IPC的使用场景:

d.Android的进程架构:每一个Android进程都是独立的,且都由两部分组成,一部分是用户空间,另一部分是内核空间,如下图:

如此设计的优点:


4.Binder机制

a.概念:

b.Android是基于Linux内核基础上设计的,却没有把管道/消息队列/共享内存/信号量/Socket等一些IPC通信手段作为Android的主要IPC方式,而是新增了Binder机制,其优点有:

方式 拷贝次数 操作难度
Binder 1 简易
消息队列 2 简易
Socket 2 简易
管道 2 简易
共享内存 0 复杂

从Android进程架构角度分析:对于消息队列、Socket和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到接收方的缓存区,一共两次拷贝,如图:

而对于Binder来说,数据从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的,节省了一次数据拷贝的过程,如图:

由于共享内存操作复杂,综合来看,Binder的传输效率是最好的。

c.Binder框架定义了四个角色:Server,Client,ServiceManager和Binder驱动。

其中Server、Client、ServiceManager运行于用户空间,Binder驱动运行于内核空间。关系如图:

下面简单介绍这四个角色:

d.代理模式Proxy:给某个对象提供一个代理对象,并由代理对象控制对原对象的访问。如图:

代理模式的组成:

推荐阅读代理模式

e.Binder 工作原理

后面会通过AIDL和Messager更深刻地体会这一工作原理。

推荐阅读Android - Binder驱动Binder设计与实现Binder系列


三、IPC方式

image

由上图可以看到,其他一些IPC方式实际都是通过Binder来实现,只不过封装方式不同。接下来分别总结其他六种IPC方式:

1.使用Bundle

a.Bundle:支持在Activity、Service和Receiver之间通过Intent.putExtra()传递Bundle数据。

Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("xxx","xxx");
intent.putExtra("data", bundle);

b.原理:Bundle实现Parcelable接口,它可方便的在不同的进程中传输。

c.注意:Bundle不支持的数据类型无法在进程中被传递。

思考下面这种情况:
Q:在A进程进行计算后的结果不是Bundle所支持的数据类型,该如何传给B进程?
A:将在A进程进行的计算过程转移到B进程中的一个Service里去做,这样可成功避免进程间的通信问题。

推荐阅读通过Bundle在Android Activity间传递数据


2.使用文件共享

a.文件共享:两个进程通过读/写同一个文件来交换数据。比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。

b.适用情况:对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。

c.虽然SharedPreferences也是文件存储的一种,但不建议采用。


3.使用AIDL

a.AIDL(Android Interface Definition Language,Android接口定义语言):如果在一个进程中要调用另一个进程中对象的方法,可使用AIDL生成可序列化的参数,AIDL会生成一个服务端对象的代理类,通过它客户端实现间接调用服务端对象的方法。

b.可支持的数据类型:

注意:除了基本数据类型,其它类型的参数必须标上方向:in、out或inout,用于表示在跨进程通信中数据的流向。

  • in
    • 表示数据只能由客户端流向服务端。
    • 服务端将会接收到这个对象的完整数据,但在服务端修改它不会对客户端输入的对象产生影响。
  • out
    • 表示数据只能由服务端流向客户端。
    • 服务端将会接收到这个对象的的空对象,但在服务端对接收到的空对象有任何修改之后客户端将会同步变动。
  • inout
    • 表示数据可在服务端与客户端之间双向流通。
    • 服务端将会接收到客户端传来对象的完整信息,且客户端将会同步服务端对该对象的任何变动。

c.两种AIDL文件:

注意:

  • 自定义的Parcelable对象必须把java文件和自定义的AIDL文件显式的import进来,无论是否在同一包内。
  • AIDL文件用到自定义Parcelable的对象,必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。

d.AIDL的本质是系统提供了一套可快速实现Binder的工具。关键类和方法:

原理图

通过此处实例具体了解AIDL实现IPC的流程:

推荐阅读Android中AIDL的工作原理

e.实现方法

总结:服务端里的某个Service给和它绑定的特定客户端进程提供Binder对象,客户端通过AIDL接口的静态方法asInterface() 将Binder对象转化成AIDL接口的代理对象,通过这个代理对象就可以发起远程调用请求。

实例使用AIDL进行进程间通信

f.可能产生ANR的情形:

g.解决客户端频繁调用服务器方法导致性能极大损耗的办法:实现观察者模式。即当客户端关注的数据发生变化时,再让服务端通知客户端去做相应的业务处理。

比如:每个客户端的请求Listener传递给服务端,服务端用一个list保存,当数据变化时服务器再依次通知,此时客户端就用Listener进行回调处理。注意要用Handler切换到主线程。

h.AIDL 解注册失败

需要用到RemoteCallBackList:Android系统专门提供的用于删除跨进程listener的接口。其内部自动实现了线程同步的功能。

推荐文章Android:学习AIDL,这一篇文章就够了


4.使用Messager

a.Messenger:轻量级的IPC方案,通过它可在不同进程中传递Message对象。

Messenger.send(Message);

相关记忆:

  • Handler:主要进行线程间的数据通信。
  • Messenger:进程间的数据通信。

b.特点

c.实现方法

实例使用Messenger实现IPC

d.Message的缺点:

解决办法:考虑使用AIDL实现IPC。

推荐阅读超简单的Binder,AIDL和Messenger的原理及使用流程


5.使用ContentProvider

a.ContentProvider:是Android提供的专门用来进行不同应用间数据共享的方式。

底层同样是通过Binder实现的。

b.注意:

基础篇组件篇之ContentProvider


6.使用Socket

a.Socket(套接字):不仅可跨进程,还可以跨设备通信。

b.使用类型

c.实现方法:TCP/UDP

d.注意:使用Socket进行通信

<uses-permission android:name="android.permission.INTERNET" />  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  

推荐阅读:这是一份很详细的Socket使用攻略

综上,以上六种IPC方式的优缺点和使用场景见下图:


四.Binder连接池

a.背景:有多个业务模块都需要AIDL来进行IPC,此时需要为每个模块创建特定的aidl文件,那么相应的Service就会很多。必然会出现系统资源耗费严重、应用过度重量级的问题。

b.作用:将每个业务模块的Binder请求统一转发到一个远程Service中去执行,从而避免重复创建Service。

c.工作原理:每个业务模块创建自己的AIDL接口并实现此接口,然后向服务端提供自己的唯一标识和其对应的Binder对象。服务端只需要一个Service,服务器提供一个queryBinder接口,它会根据业务模块的特征来返回相应的Binder对像,不同的业务模块拿到所需的Binder对象后就可进行远程方法的调用了。流程如图:

d.实现方式:

实例细说Binder连接池

现在可以回答以下问题:

Q:在Android开发中提高开发效率的方法?
A:使用Binder连接池,避免反复创建Service,统一管理和维护AIDL。

推荐阅读Android的IPC机制Android跨进程通信IPC


希望这篇文章对你有帮助~

上一篇 下一篇

猜你喜欢

热点阅读