android 中的ipc(进程间通信)

1.bundle :
简单易用 但是只能传输Bundle支持的对象 常用于四大组件间进程间通信
2.文件共享:
简单易用 但不适合在高并发的情况下 并且读取文件需要时间 不能即时通信 常用于并发程度不高 并且实时性要求不高的情况
3.AIDL :
功能强大 支持一对多并发通信 支持即时通信 但是使用起来比其他的复杂 需要处理好多线程的同步问题 常用于一对多通信 且有RPC 需求的场合(服务端和客户端通信)
4.Messenger :
功能一般 支持一对多串行通信 支持实时通信 但是不能很好处理高并发情况 只能传输Bundle支持的类型 常用于低并发的无RPC需求一对多的场合
5.ContentProvider :
在数据源访问方面功能强大 支持一对多并发操作 可扩展call方法 可以理解为约束版的AIDL 提供CRUD操作和自定义函数 常用于一对多的数据共享场合
6.Socket :
功能强大 可以通过网络传输字节流 支持一对多并发操作 但是实现起来比较麻烦 不支持直接的RPC 常用于网络数据交换

总结起来
当仅仅是跨进程的四大组件间的传递数据时 使用Bundle就可以 简单方便
当要共享一个应用程序的内部数据的时候 使用ContentProvider实现比较方便
当并发程度不高 也就是偶尔访问一次那种 进程间通信 用Messenger就可以
当设计网络数据的共享时 使用socket
当需求比较复杂 高并发 并且还要求实时通信 而且有RPC(远程过程调用协议)需求时 就得使用AIDL了
文件共享的方法用于一些缓存共享 之类的功能

Android 从下而上分了内核层、硬件抽象层、系统服务层、Binder IPC 层、应用程序框架层
Android 中「应用程序框架层」以 SDK 的形式开放给开发者使用,「系统服务层」中的核心服务随系统启动而运行,通过应用层序框架层提供的 Manager 实时为应用程序提供服务调用。系统服务层中每一个服务运行在自己独立的进程空间中,应用程序框架层中的 Manager 通过 Binder IPC 的方式调用系统服务层中的服务。

Binder IPC 的架构

下面我们就来看看 Binder IPC 的架构是怎样的

2628445-8e398eb2bcd029ba.png.jpg

Binder IPC 属于 C/S 结构,Client 部分是用户代码,用户代码最终会调用 Binder Driver 的 transact 接口,Binder Driver 会调用 Server,这里的 Server 与 service 不同,可以理解为 Service 中 onBind 返回的 Binder 对象,请注意区分下。

  • Client:用户需要实现的代码,如 AIDL 自动生成的接口类
  • Binder Driver:在内核层实现的 Driver
  • Server:这个 Server 就是 Service 中 onBind 返回的 IBinder 对象

需要注意的是,上面绿色的色块部分都是属于用户需要实现的部分,而蓝色部分是系统去实现了。也就是说 Binder Driver 这块并不需要知道,Server 中会开启一个线程池去处理客户端调用。为什么要用线程池而不是一个单线程队列呢?试想一下,如果用单线程队列,则会有任务积压,多个客户端同时调用一个服务的时候就会有来不及响应的情况发生,这是绝对不允许的。

对于调用 Binder Driver 中的 transact 接口,客户端可以手动调用,也可以通过 AIDL 的方式生成的代理类来调用,服务端可以继承 Binder 对象,也可以继承 AIDL 生成的接口类的 Stub 对象。这些细节下面继续接着说,这里暂时不展开。

切记,这里 Server 的实现是线程池的方式,而不是单线程队列的方式,区别在于,单线程队列的话,Server 的代码是线程安全的,线程池的话,Server 的代码则不是线程安全的,需要开发者自己做好多线程同步。

小结

  • Binder IPC 属于 C/S 架构,包括 Client、Driver、Server 三个部分
  • Client 可以手动调用 Driver 的 transact 接口,也可以通过 AIDL 生成的 Proxy 调用
  • Server 中会启动一个「线程池」来处理 Client 的调用请求,处理完成后将结果返回给 Driver,Driver 再返回给 Client

这里就回答了开篇提问的两个问题:Service 中通过 AIDL 提供的接口并不是线程安全的,同理 ContentProvider 底层也是使用 Binder,同样不是线程安全的,至于是否需要做多线程保护,看业务而定,最好是做好多线程同步,以防万一。

什么是Binder

这个问题很多文章都有解释,比如:Binder是Android跨进程通信方式,它实现了IBinder接口,是ServiceManager连接各种Manager(如WindowManager、ActivityManager等)的桥梁。但是我觉得这些说法还是过于抽象。刚接触Binder时,看到这些定义还是一头雾水,只是内心觉得Binder很牛逼、很底层,仅此而已。

那么应该怎么去理解Binder呢?我不打算介绍这个概念,而是介绍Binder是怎么来到Android世界的。我是这样理解的:Android团队想要实现进程之间的通信,需要解决以下几个问题:

    如何知道客户端需要调用哪个进程以及该进程中的函数
    客户端如何将函数形参发送给远程进程中的函数,以及如何将远程进程函数计算结果返回客户端
    如何去屏蔽底层通信细节,让实现客户端调用远程函数就像调用本地函数一样

第一个问题,很容易解决,只要给每个需要远程通信的类唯一标识就可以通过包名+类名的字符串就可以做到,然后在类里面给每个函数编号即可对函数唯一编码。第二个问题,定义一个可打包的接口Parcelable,这个接口提供2个重要函数,分别是将对象中的属性写入到数组和从数组中的数据还原对象,每个可以发送到远程函数作为形参的对象只需实现Parcelable对象即可。Parcelable具体使用不再本文讨论范围。第三个问题,为了屏蔽进程之间的通信细节,那么Android团队肯定在想,定义一个类,由这个类来实现这些细节。这个类应该做哪些事情呢?首先,这个类得帮用户发送远程请求并将拿到返回结果提交给用户,这是最重要的功能了,有了这个功能,妈妈再也不用担心我的进程通信。其次,如果我想实现服务端,什么时候客户端调用我了,这些细节不用用户操心。当然,这个类还要帮用户封装更多细节。既然打算定义这个类了,那总得取个响当当的名称吧,什么?你说取名为Binder,好吧,那就叫Binder吧。Binder类既然封装很多功能,那该怎么用这个类呢?让客户端去继承还是服务端继承呢?答案是服务端。接下来有个约定,本文后面所指的Binder类都是指远程服务端的对象。服务端想要实现被跨进程访问,就必须继承Binder类。
首先我们看看我们的程序跨进程调用系统服务的简单示例,实现浮动窗口部分代码:

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);  

系统服务都是运行在systemServer进程中,因此我们调用系统服务都是跨进程的调用。第2行代码中,得到的wm是WindowManager对象的引用,第6行调用WindowManager的addView函数,将触发远程调用,调用的是运行在systemServer进程中的WindowManager的addView函数。是不是很想知道addView发生了什么?我们先看看Binder机制吧!看完Binder原理,再解释!

Binder机制

先看看一般执行过程

代码执行过程

假设你已经创建好服务端类MyService、客户端类MyClient。在客户端持有MyService的引用,并且调用了MyService的func函数,那么Android内部调用过程如下:


2154124-bd83d477ef791b81.png

看了这个图以后,相信你对你的代码在调用远程进程函数时有个全局的认识。这张图有一点很重要,就是客户端当前线程会被挂起!因此,如果远程进程是执行长时间的运算,请不要使用主线程去调用远程函数,以防止ANR。

Binder的C/S架构

上面一节我们对远程进程调用代码执行过程有个初步了解,在Android开发中,我们大量使用到了系统Service,比如媒体播放、各种传感器以及WindowManagerService等等等等(太多了~)。那么Android是怎么管理这些服务,并且让用户跨进程调用这些服务呢?首先我们看看调用系统服务的过程。在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,然后通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。


2154124-746b33a4cfad1b93.png

Binder驱动实现原理

一直以来,我有个困惑!!!这个困惑让我迷茫了很久:客户端持有远程进程的某个对象引用,然后调用引用类中的函数,远程进程的函数就执行了。我在想,凭什么?学过操作系统都知道,不同的进程之间是不共享资源的。也就是说,客户端持有的这个对象跟远程进程中的实际对象完全是两个不同的对象。客户端调用引用的对象跟远程进程半毛钱关系都没有,凭啥远程进程就调用了执行了?相信也有一部分人跟我有同样的困惑!仔细研读一下下面这张图,相信你会豁然开朗!


2154124-8788ce62c0c7384e.png

服务端跨进程的类都要继承Binder类。我们所持有的Binder引用(即服务端的类引用)并不是实际真实的远程Binder对象,我们的引用在Binder驱动里还要做一次映射。也就是说,设备驱动根据我们的引用对象找到对应的远程进程。客户端要调用远程对象函数时,只需把数据写入到Parcel,在调用所持有的Binder引用的transact()函数,transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存,把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类。远程进程Binder对象执行完成后,将得到的写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程。

Binder机制运用

好了,现在对Binder机制已经理解了,我们再看看Android是怎么运用Binder的。再现前面代码:

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams); 

这段代码前面已经出现过。getSystemService(getApplication().WINDOW_SERVICE);函数内部原理就是向ServiceManager查询标识符为getApplication().WINDOW_SERVICE的远程对象的引用。即WindowManager对象的引用,这个引用的真正实现是WindowManager的某个代理。得到这个引用后,在调用addView时,真正的实现是在代理里面,代理把参数打包到Parcel对象中,然后调用transact函数(该函数继承自Binder),再触发Binder驱动的一系列调用过程,在Binder驱动实现原理一节中有具体介绍,忘记了的同学可以返回继续看。关于Binder的代理对象,可以参考AIDL工具生成的代码,这里不再具体介绍。

http://blog.csdn.net/flyingqr/article/details/71411795

http://blog.csdn.net/singleton1900/article/details/8434643

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容