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");  
这样就可以了~

最新文章

  1. HTML5 & CSS3初学者指南(1) – 编写第一行代码
  2. Axure RP = Axure Rapid Prototyping
  3. springmvc+spring+hibernate
  4. RestSharp简单扩展
  5. 2015年12月28日 Java基础系列(六)流
  6. WPF笔记
  7. kafka basic commands
  8. BZOJ4295 : [PA2015]Hazard
  9. 消息队列MQ
  10. C# 汉字转拼音(转)
  11. [C++]cin读取回车键
  12. weblogic11g 安装——linux 无图形界面
  13. Autofac 之 基于 Castle DynamicProxy2 的 Interceptor 功能
  14. 2.x ESL第二章习题 2.8
  15. bzoj2839 集合计数(容斥)
  16. OO第四次作业总结
  17. 字符串a-b
  18. VS2015/Visual Studio快捷键无效问题
  19. Visual stuio2015 升级 Update 3+安装.Net Core 安装包之后,无法创建Mvc项目
  20. Solr7.4.0的API(Solrj)操作

热门文章

  1. 如何安装Oracle Database 11g数据库
  2. angularjs自定义日期过滤器,如:周日(前天 21:24)
  3. C++:MEMSET的大坑三两事
  4. Poweroff – 很好很强大的定制关机工具
  5. 【学习笔记】【oc】类的包装类 协议 category
  6. MVC3.0,路由设置实现伪静态IIS中404错误
  7. java1200例-文字的探照灯效果
  8. JS--图片轮播效果
  9. uva 10026 Problem C: Edit Step Ladders
  10. LinkedList和ArrayList的区别