1、IBinder和Binder是什么鬼?
我们来看看官方文档怎么说:
中文翻译:
IBinder是远程对象的基本接口,是为了高性能而设计的轻量级远程调用机制的核心部分。
但他不仅用于远程调用,也用于进程内调用。
该接口定义了与远程对象间交互的协议。但不要直接实现这个接口,而是继承 extends Binder。
IBinder主要的API是transact(),与之对应的API是Binder.onTransact()。
通过前者,你能向远程IBinder对象发送发出调用,后者使你的远程对象能够响应接收到的调用。
IBinder的API都是同步执行的,比如transact()直到对方的Binder.onTransact()方法调用玩后才返回。
调用发生在进程内时无疑是这样的,而在进程间时,在IPC的帮助下,也是同样的效果。
通过transact()发送的数据是Parcel,Parcel是一种一般的缓冲区,除了有数据外还带有一些描述它内容的元数据。
元数据用于管理IBinder对象的引用,这样就能在缓冲区从一个进程移动到另一个进程时保存这些引用。
这样就保证了当一个IBinder被写入到Parcel并发送到另一个进程中,如果另一个进程把同一个IBinder的引用回发到原来的进程,那么这个原来的进程就能接收到发出的那个IBinder的引用。
这种机制使IBinder和Binder像唯一标志符那样在进程间管理。
系统为每个进程维护一个存放交互线程的线程池。这些交互线程用于派送所有从另外进程发来的IPC调用。
例如:当一个IPC从进程A发到进程B,A中那个发出调用的线程(这个应该不在线程池中)就阻塞在transact()中了。
进程B中的交互线程池中的一个线程接收了这个调用,它调用Binder.onTransact(),完成后用一个Parcel来做为结果返回。
然后进程A中的那个等待的线程在收到返回的Parcel后得以继续执行。
实际上,另一个进程看起来就像是当前进程的一个线程,但不是当前进程创建的。
Binder机制还支持进程间的递归调用。
例如,进程A执行自己的IBinder的transact()调用进程B的Binder,而进程B在其Binder.onTransact()中又用transact()向进程A发起调用,那么进程A在等待它发出的调用返回的同时,还会用Binder.onTransact()响应进程B的transact()。
总之Binder造成的结果就是让我们感觉到跨进程的调用与进程内的调用没什么区别。
当操作远程对象时,你经常需要查看它们是否有效,有三种方法可以使用:
1、transact()方法将在IBinder所在的进程不存在时抛出RemoteException异常。
2、如果目标进程不存在,那么调用pingBinder()时返回false。
3、可以用linkToDeath()方法向IBinder注册一个IBinder.DeathRecipient,在IBinder代表的进程退出时被调用。
简单的说就是:IBinder是Android给我们提供的一个进程间通信的一个接口,是Android中实现IPC(进程间通信)的一种方式!
2、Binder机制浅析
Android中的Binder机制由一系列系统组件构成:Client、Server、Service Manager 和 Binder 驱动程序
大概调用流程如下,另外Service Manager比较复杂,这里并不详细研究!
流程解析:
-> Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打包成Parcel对象;
-> 然后代理接口把该Parcel对象发送给内核中的Binder driver;
-> 然后Server会读取Binder Driver中的请求数据,假如是发送给自己的,解包Parcel对象,处理并将结果返回;
PS:代理接口中的定义的方法和Server中定义的方法是一一对应的,
另外,整个调用过程是一个同步的,即Server在处理时,Client会被Block(锁)住!
而这里说的代理接口的定义就是等下要说的AIDL(Android接口描述语言)!
3、为何Android使用Binder机制来实现进程间的通信?
可靠性:在移动设备上,通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口,媒体播放,传感器,无线传输。这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问,如若在底层架设一套协议来实现Client-Server通信,增加了系统的复杂性。在资源有限的手机 上来实现这种复杂的环境,可靠性难以保证。
传输性能:socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。
安全性:Android是一个开放式的平台,所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID,这样不可靠,容易被恶意程序利用。而我们要求由内核来添加可靠的UID。
所以,出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。
——摘自:Android中的Binder机制的简要理解
当然,作为一个初级的开发者我们并不关心上述这些,Binder机制给我们带来的最直接的好处就是:
我们无需关心底层如何实现,只需按照AIDL的规则,自定义一个接口文件,然后调用调用接口中的方法,就可以完成两个进程间的通信了!
4、AIDL使用详解
1)AIDL是什么?
嘿嘿,前面我们讲到IPC这个名词,他的全名叫做:跨进程通信(interprocess communication)。
因为在Android系统中,每个应用程序都运行在自己的进程中,进程之间一般是无法直接进行数据交换的
而为了实现跨进程,Android给我们提供了上面说的Binder机制,而这个机制使用的接口语言就是AIDL(Android Interface Definition Language)
他的语法很简单,而这种接口语言并非真正的编程语言,只是定义两个进程间的通信接口而已
而生成符合通信协议的Java代码则是由Android SDK的platform-tools目录下的aidl.exe工具自动生成的
生成的对应的接口文件被放在在gen目录下,一般是:Xxx.java的接口!
而在该接口中包含一个Stub的内部类,该类实现了IBinder接口与自定义的通信接口,这个类将会作为远程Service的回调类!
2)AIDL编写注意事项
接口名字需要与aidl文件名相同
接口和方法前面不要加访问权限修饰符:public 、private、protected等,也不能用static、final
AIDL默认支持的类型包括Java基本类型,String,List,Map,CharSequence
除此之外的其他类型都需要import声明,对于使用自定义类型作为参数或者返回值,自定义类型需要实现Parcelable接口
自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中
另外,非原始类型中,除了String和CharSequence以外,其余均需要一个方向指示符。
方向指示符包括in、out、和inout。in表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值。
假如你使用的是Android Studio的话,不同于Eclipse,如果你按照Eclipse那样创建一个AIDL文件,会发现并没有编译生成对应的XXX.java文件
AS下创建AIDL需要在main目录下新建一个aidl文件夹,然后定义一个和aidl包名相同的包,最后创建一个aidl文件,接着按ctrl + f9重新编译,就可以了!
3)服务端注意事项
使用AIDL时,Service内部类MyBinder不再是【extends Binder implements IBinderInterface】而是直接【extends IBinderInterface.Stub】
若服务端需要传递复杂数据类型(即实现Parcelable接口的Bean),则必须改为【extends Stub】
4)客户端注意事项
和绑定本地Service不同,绑定远程Service的ServiceConnection并不能直接获取Service的onBind( )方法返回的IBinder对象,只能返回onBind( )方法所返回的代理对象,需要做如下处理:
mIBinder = IBinderInterface.Stub.asInterface(service);
其他的和绑定本地服务完全一样。
5、Parcelable接口简介
相信用过序列化的基本上都知道这个接口了,除了他还有另外一个Serializable,同样是用于序列化的,只是Parcelable更加轻量级,速度更快,但是写起来就有点麻烦了。
首先需要实现:writeToParcel 和 readFromPacel 方法(貌似一般不需要重写 readFromPacel )
writeToParcel 方法是将数据(对象的成员)写入到外部提供的包裹(Parcel)中
readFromPacel 方法则从包裹中读取对象
请注意:写入属性顺序需与读取顺序相同
接着需要在该类中添加一个名为CREATOR的 static final 属性,该属性需要实现 android.os.Parcelable.Creator 接口
再接着需要重写接口中的两个方法:
createFromParcel方法实现从Parcel中读取数据,并创建出JavaBean的实例的功能。参数列表要和构造方法中的参数列表相对应
newArray创建一个类型为T,长度为size的数组,代码只有一句简单的return new T[size]。估计本方法是供外部类反序列化本类数组使用。
最后,describeContents这个直接返回0即可,不用理他
6、Android 5.0后Service一些要注意的地方
今天在隐式启动Service的时候,遇到这样一个问题
原来5.0后有个新的特性,就是:Service Intent must be explicit(明确的),就是不能隐式去启动Service咯
文档说明:
解决的方法也很简单,比如这样StartService:
startService(new Intent("YourAction"));
程序直接crash掉,要写成这样:
startService(new Intent(getApplicationContext(), MyRemoteService.class));
如果是BindService,在
Intent service = new Intent("YourAction");
的基础上,要加上包名:
service.setPackage("com.bqt.aidlservice");
这样就可以了~
- Android查缺补漏(IPC篇)-- 进程间通讯之AIDL详解
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- Android AIDL 进行进程间通讯(IPC)
编写AIDL文件时,需要注意: 1.接口名和aidl文件名相同. 2.接口和方法前不用加访问权限修饰符 (public.private.protected等,也不能用final.static). 3. ...
- High Performance Networking in Google Chrome 进程间通讯(IPC) 多进程资源加载
小结: 1. 小文件存储于一个文件中: 在内部,磁盘缓存(disk cache)实现了它自己的一组数据结构, 它们被存储在一个单独的缓存目录里.其中有索引文件(在浏览器启动时加载到内存中),数据文件( ...
- QSharedMemory共享内存实现进程间通讯(IPC)及禁止程序多开
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QSharedMemory共享内存实现进程间通讯(IPC)及禁止程序多开 本文地址:h ...
- 服务 远程服务 AIDL 进程间通讯 IPC
Activity aidl接口文件 package com.bqt.aidlservice; interface IBinderInterface { /* 更改文件后缀为[.aidl]去掉 ...
- 服务 远程服务 AIDL 进程间通讯 IPC 深化
示例 aidl接口文件 package com.bqt.aidlservice.aidl; parcelable Person; package com.bqt.aidlservice.aidl; ...
- ipc 进程间通讯的AIDL
1.什么是aidl:aidl是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间 ...
- 进程间通讯IPC的几种方式总结
Linux进程间的通讯 Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同.前者对Unix早期的进程间通信 ...
- Android查缺补漏(IPC篇)-- 进程间通讯之Socket简介及示例
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8425736.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
随机推荐
- Ubuntu小私房(4)--Linux系统目录结构
Linux目录结构是Linux学习者必须了解的知识,Linux的目录与Windows又有很大的不同,所以搞清楚Linux目录结构是关键. Linux文件类型 (部分转自ChinaBytel) linu ...
- SSM框架搭建java.lang.ClassNotFoundException: org.springframework.http.converter.json.MappingJacksonHttpMessageConverter
在搭建 spring springMVC Mybatis 时候出错 将org.springframework.http.converter.json.MappingJacksonHttpMessage ...
- phpcms v9二次开发之模型类的应用(1)
在<phpcms二次开发之模型类model.class.php>中讲到了模型类的建立方法,接下来我讲一下模型类的应用. 前段时间我基于phpcms v9开发了一个足球网.足球网是 ...
- phpcms v9 表单向导里添加图片字段,提示会话过期,请重新登陆
用 phpcms v9 制作前端用户提交信息时,有个图片上传的栏目. 如果注释 /phpcms/modules/attachment/attachment.php 20//判断是否登录 21 if(e ...
- 【学习笔记】【Foundation】字典
字典NSDictionary: 用于保存具有映射关系的数据:key-value: 创建NSDictionary的常用方法: dictionary: dictionaryWithContentsOfFi ...
- 转: pthread_create()
pthread_create函数 原型:int pthread_create((pthread_t *thread, pthread_attr_t *attr, void *(*start ...
- AD14中如何定义PCB尺寸大小(不同于AD9和10)
以前用的着软件基本都是停留在9和10,14用的少,虽然14增加了很多功能,但是也带来了不变,虽然可以打开各种其他格式的文件,但是有些地方的改动不容忽略,在一个完整的板子下来需要用到的每个地方的点也会有 ...
- GUI(主)线程与子线程之间的通信(用信号槽通讯)
在主线程上,可以控制子线程启动,停止,清零 如果子线程启动的话,每一秒钟会向主线程发送一个数字,让主线程更新界面上的数字. 程序截图: 上代码: #include <QtGui> #inc ...
- android中使用PopupWindow实现弹出窗口菜单
结合上篇android中使用ViewPager实现图片拖动,我们实现了点击“帮助”按钮的功能,这一篇则是接着上一篇,让我们一起来完成“我的”按钮的功能,这一功能,则是使用PopupWindow来实现弹 ...
- 8.2.1.15 ORDER BY Optimization ORDER BY 优化
8.2.1.15 ORDER BY Optimization ORDER BY 优化 在一些情况下, MySQL 可以使用一个索引来满足一个ORDER BY 子句不需要做额外的排序 index 可以用 ...