1。前言最近因为工作的原因,接触到更多的框架部分,难免要和Binder打交道,整理一些相关的知识,但是在准备写这篇文章的时候,我还是一个有点恐慌。而且,整个Binder机制的复杂性,不是三言两语能描述清楚的,怕自己的理解有偏差,误导一些小伙伴(ps:反正没人看。。。心碎),所以我也参考一下到很多信息。本文主要从Android开发的角度分析Binder在java层的一些知识原理,给大家形成一个完整的概念,比如AIDL的实现原理,Binder是如何通信的等等。文中有很多篇文章,请耐心观看的朋友可以看下一篇文章,介绍Activity的启动过程和Android中的Hook技术:震撼!活动不需要注册?教你Hook2.Binder概述2.1为什么Android选择BinderAndroid是基于Linux内核的,所以Android可以利用Linux原有的一些方法,如管道、共享内存、套接字等,实现进程间通信,但Android仍然以Binder为主要机制,可见Binder有着无可比拟的优势。其实进程通信大概有两个因素。一是性能和传输效率。传统的流水线队列模式使用内存缓冲区。数据先从senderbuffer复制到kernel打开的buffer,然后从kernelbuffer复制到receiverbuffer,至少有两个复制过程,socket知道传输效率低,开销大,用于跨网络进程交互,虽然不需要拷贝共享内存。两者都是安全问题。Android作为一个拥有众多开发者的开放平台,其来源非常广泛。确保终端安全非常重要。传统的IPC通信方式没有措施,基本依赖上层协议。无法确认对方的可靠身份。Android为每一个安装的应用分配了自己的UID,所以进程的UID是识别进程身份的重要标志。传统IPC只能在数据包中发送相似的UID。但是也容易被拦截和恶意攻击。套接字需要暴露自己的ip和端口。知道了这些恶意程序,就可以任意访问了。综上所述,Android需要一种高效率、高安全的进程通信方式,即Binder。Binder只需要一份,性能仅次于共享内存。而且采用传统的C/S结构,稳定性也很高。不用说,发送和添加UID/PID是高度安全的。2.2Binder实现机制2.2.1进程隔离我们知道进程不能直接交互,每个进程都有自己的数据,操作系统为了保证自身的安全和稳定,将系统内核空间和用户空间分开来保证用户程序进程崩溃时不会影响整个系统。简单的说,内核空间(Kernel)就是系统内核运行的空间,用户空间(UserSpace)就是用户程序运行的空间。为了保证安全,它们是隔离的,所以用户空间的进程需要通过内核空间来驱动整个进程进行交互。2.2.2C/S结构Binder是基于C/S机制的。要实现这样的机制,服务端必须要有一个特定的节点来接受客户端的请求,也就是入口地址,比如输入一个URL,通过DNS解析出对应的ip,然后访问,这个就是提供的节点地址由服务端,而Binder与传统的C/S不一样,Binder本身作为服务端提供的节点,客户端获取对应的Binder实体对象的地址来访问Server,对于客户端来说,如何获取这个地址并建立整个通道是整个交互的关键,而Binder作为Server中的一个实体,对象提供了一系列的方法来实现server和client之间的请求,只要client拿到这个reference或与此方法的代理对象的引用,可以进行通信。引用AndroidBanderdesignandimplementation-design一文总结一下:引入面向对象思想,将进程间通信转化为通过对象的引用来调用Binder对象的方法,其独特之处在于Binder对象是一个Binder对象,可以跨进程引用的对象,它的实体位于一个进程中,但它的引用却分散在系统的各个进程中。最吸引人的是这个引用可以像java中的引用一样是强类型也可以是弱类型,并且可以从一个进程传递到其他进程,这样大家就可以访问同一个Server,就像传递一个对象或者引用一样与分配给另一个参考相同。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行在同一个面向对象程序中。形形色色的Binder对象和点缀的引用,仿佛是绑定各种应用的胶水,这也是Binder在英文中的本意。2.2.3Binder通信模型Binder基于C/S结构定义了四种角色:Server、Client、ServerManager、Binderdriver。前三个在用户空间,即它们之间不能直接交互。Binder驱动属于内核空间,属于整个通信的核心。虽然称为驱动程序,但实际上与硬件无关,但实现方法与驱动程序类似。驱动程序负责建立进程间的Binder通信。Binder在进程之间。进程间传递、Binder引用计数管理、数据包传递和进程间交互等一系列底层支持。ServerManager的作用?我们知道ServerManager也是一个属于用户空间的进程。它的主要功能是作为Server和client之间的桥梁。Client可以从ServerManager获取Server中Binder实体的引用。这么说可能有点含糊。举个简单的例子,我们访问,www.baidu.com,显示百度首页。首先,我们知道这个页面必须发布在百度服务器上。DNS通过你的地址解析出对应的ip地址,然后访问对应的页面,然后返回数据给客户端,完成交互。这个和Binder的C/S很像。这里的DNS就是对应的ServerManager。首先Server中的Binder实体对象将自己的引用(即ip地址)注册到ServerManager中,客户端通过一个特定的key(即百度这个URL)绑定到这个引用上。ServerManager维护一个类似MAP的表,用于一一对应。通过这个key,可以从ServerManager中获取Server中Binder的引用。对应Android开发,我们知道很多系统服务都是通过Binder与AMS交互的,比如获取音量服务:AudioManageram=(AudioManager)context.getSystemService(Context.AUDIO_SERVICE);细心的朋友应该会发现,ServerManager和Server也是两个不同的进程,而Server需要注册ServerManager不也涉及到进程间通信吗?目前进程间通信的实现也是需要进程间通信的。你在开玩笑吧……别着急,Binder的巧妙之处在于,当ServerManager作为服务端时,它提供的Binder是比较特别的。它没有名称,也不需要注册。当一个进程使用BINDER_SET_CONTEXT_MGR命令将自己注册为SMgr时,Binder驱动会自动为其创建一个Binder实体。Binder引用在所有Client中固定为0,不需要通过其他方式获取。也就是说,如果一个server要向servermanager注册自己的binder,就必须通过引用号0和servermanager的binder通信。有朋友又要问了,server和client属于两个不同的流程,client怎么才能拿到server的Object呢,不妨看看下面的交互图。从上图可以清楚的看到整个交互过程。本来binder的引用是从SM中获取的,经过binder驱动层处理后,返回一个代理对象给客户端。实际上,如果客户端和服务器在同一个进程中,则返回当前的binder对象。如果客户端和服务器不在同一个进程中,则返回一个代理对象给客户端。有兴趣的可以看看源码。2.2.4Binder角色定位Binder本质上只是提供了一种通信方式,与我们要实现的目标无关。为了实现这个服务,我们需要定义一些接口,让客户端可以远程调用这个服务,因为是跨进程的,这时候就需要设计代理模式,基于接口函数,客户端和服务端实现接口功能,服务端是服务的真正实现,客户端是远程调用。从Server进程的角度来看,Binder是一个已经存在的实体对象。客户端使用transact()函数,由Binder驱动,最终回调Binder实体的onTransact()函数。从Client进程的角度来看,Binder是指Binder代理对象,它是Binder实体对象的远程代理,通过Binder驱动进行交互。代码的形式可以让大家对Binder有更深入的了解。在日常开发中,一提到进程间通信,我们首先想到的可能就是AIDL,但不知道有没有小伙伴和我有一样的感受。.第一次写AIDL是骗人的。通过.aidl文件,编译器自动生成代码,生成一个java文件,里面有Stub类和Proxy类。完全不懂机制,我们要理解和学习真的不容易,为了加深理解,我们放弃了aidl,手写了一段通信代码。首先,我们需要定义一个接口服务,也就是服务端必须要提供给客户端的上述能力。定义一个继承自IInterface的接口,代表服务器的能力。publicinterfacePersonMangerextendsIInterface{voidaddPerson(PersonmPerson);ListgetPersonList();}复制代码接下来我们在Server中定义一个Binder实体对象。首先,我们必须继承Binder,其次,我们需要实现上面定义的服务接口。publicabstractclassBinderObjextendsBinderimplementsPersonManger{publicstaticfinalStringDESCRIPTOR="com.example.taolin.hellobinder";publicstaticfinalintTRANSAVTION_getPerson=IBinder.FIRST_CALL_TRANSACTION;publicstaticfinalintTRANSAVTION_addPerson=IBinder.FIRST_CALL_TRANSACTION+1;公共静态IBinder。查询本地接口(描述符);if(null!=iInterface&&iInterfaceinstanceofPersonManger){return(PersonManger)iInterface;}returnnewProxy(mIBinder);}@OverrideprotectedbooleanonTransact(intcode,@NonNullParceldata,@NullableParcelreply,intflags)throwsRemoteException{开关(代码){caseINTERFACE_TRANSACTION:reply.writeString(DESCRIPTOR);返回真;案例TRANSAVTION_getPerson:data.enforceInterface(DESCRIPTOR);List结果=this.getPersonList();回复.writeNoException();reply.writeTypedList(结果);返回真;caseTRANSAVTION_addPerson:data.enforceInterface(DESCRIPTOR);人物arg0=null;如果(data.readInt()!=0){arg0=Person.CREATOR.createFromParcel(data);}this.addPerson(arg0);回复.writeNoException();返回真;}returnsuper.onTransact(code,data,reply,flags);}@OverridepublicIBinderasBinder(){returnthis;对于IBinder对象,使用queryLocalInterface方法查找本地Binder对象。如果返回的对象是PersonManger,说明客户端和服务端在同一个进程,直接返回。如果不是,则将其返回给代理对象当然作为代理对象,也是需要实际服务接口publicclassProxyimplementsPersonManger{privateIBindermIBinder;publicProxy(IBindermIBinder){this.mIBinder=mIBinder;}@OverridepublicvoidaddPerson(PersonmPerson){Parceldata=Parcel.obtain();包裹重播=Parcel.obtain();尝试{data.writeInterfaceToken(DESCRIPTOR);如果(mPerson!=null){data.writeInt(1);mPerson.writeToParcel(数据,0);}else{data.writeInt(0);}mIBinder.transact(BinderObj.TRANSAVTION_addPerson,data,replay,0);重播.readException();}catch(RemoteExceptione){e.printStackTrace();}最后{replay.recycle();数据回收();}}@OverridepublicListgetPersonList(){包裹数据=Parcel.obtain();包裹重播=Parcel.obtain();列表<人>结果=空;尝试{data.writeInterfaceToken(DESCRIPTOR);mIBinder.transact(BinderObj.TRANSAVTION_getPerson,数据,回放,0);重播.readException();结果=replay.createTypedArrayList(Person.CREATOR);}catch(RemoteExceptione){e.printStackTrace();}最后{replay.recycle();数据回收();}returnresult;}@OverridepublicIBinderasBinder(){returnnull;}}复制代码这里代理对象的本质是客户端最终得到的代理服务。通过它,您可以与服务器进行通信。先将数据通过Parcel序列化,然后调用remote.transact()传递方法代码和数据,对应的调用会在Server中的onTransact()回调,然后是我们的Server进程。onBind方法返回的是mStub对象,也是Server中的Binder实体对象voidonCreate(){mPeople.add(newPerson());super.onCreate();}@Nullable@OverridepublicIBinderonBind(Intentintent){returnmStub;}privateBinderObjmStub=newBinderObj(){@OverridepublicvoidaddPerson(PersonmPerson){if(mPerson==null){mPerson=newPerson();Log.e(TAG,"空对象");}mPeople.add(mPerson);Log.e(TAG,mPeople.size()+"");}@OverridepublicListgetPersonList(){returnmPeople;}};}复制代码最后,在客户端进程中,bindService传入一个ServiceConnection对象,在与服务端建立连接时,通过我们定义的BinderObj对象的asInterface方法返回一个代理,然后调用该方法来交互publicclassMainActivityextendsAppCompatActivity{privatebooleanisConnect=false;privatestaticfinalStringTAG="MainActivity";privatePersonMangerpersonManger;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState)(R.layout.activity_main);开始();findViewById(R.id.textView).setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){if(personManger==null){Log.e(TAG,"connecterror");返回;}personManger.addPerson(新人());Log.e(TAG,personManger.getPersonList().size()+"");}});}privatevoidstart(){Intentintent=newIntent(this,ServerSevice.class);bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);}privateServiceConnectionmServiceConnection=newServiceConnection(){@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){Log.e(TAG,"连接成功");是连接=真;personManger=BinderObj.asInterface(服务);列表<人>personList=personManger.getPersonList();Log.e(TAG,personList.size()+"");}@OverridepublicvoidonServiceDisconnected(ComponentNamename){日志。e(TAG,"连接失败");连接=假;}};}这样的话,完成一次的进程之间的交互就完成了~是不是没有想象中那么难?最后建议大家在不使用AIDL手写的情况下,实现Client和Server进程的通信,加深对Binder通信过程的理解