标签:android style blog http color io os 使用 ar
终于还是得写一篇关于Binder的文章了。从最初接触Android到花大把时间研究Android源码,Binder一直是分析道路的拦路虎。看了几本最流行的Android源码分析书籍,每次基本上都不能把Binder相关知识看完、读透。好在一直没有放弃,第一次理解不了就跳过,下一次重新读,每次读都有新的收获。现在是时候整理整理了。
我理解的Binder是什么?一种IPC(跨进程通信)的实现方式。注意“跨进程”,表明数据从一个进程“流向”了另一个进程。首先要了解为什么跨进程那么难?因为应用程序的地址空间都是虚地址,通过映射到物理内存空间。假如应用程序都直接在物理内存空间操作,那应用程序跨进程就非常方便了,直接传递物理地址就好了。但是,不同应用程序使用的是通过内存管理器作映射的虚拟内存空间,至于映射到哪个物理内存区域,这是未知的,从而使得跨进程传递数据变得不那么方便,但也不是没有办法。Linux中典型的管道、消息队列、共享内存等都可以实现跨进程通信。Android采用了Binder方式,自然有它的优势,所谓“灵活、方便”,这个优势还在慢慢体会中。
要深刻理解android的Binder机制,有几点是需要理解的:
如果看过Service实现,或者各种类的继承关系,它们是有一定的层次关系的。现在采用“从下往上”的逐层分析,在看源码时,个人发现“从下往上”往往可以取得就好的效果。结合《深入理解android 卷1》第六章来分析。
有设备就离不开设备驱动,Binder实际上就是一个包含驱动、协议的伪设备,通过常用的对设备的操作,如open、ioctl、mmap等,可以操作Binder这个“设备”。其中关键点为:通过mmap可以把一块虚拟地址与物理地址映射起来,这时候,A进程与Binder共享同一块物理内存,如果另一个B进程也通过Binder共享了这块内存,那么A、B两进程就共享了同一块内存——这就是不同应用程序共享内存的(通过Binder)的原理。还有有一句话就是,如果到这里,两个进程之间已经完全可以通过这个Binder来通信了,当然实现细节肯定很麻烦。故要做封装!!根据上面所说的第3点,这里仍然在描述通信细节,还没有涉及到业务。Binder驱动本身比较复杂,不作分析。可以简单化它:通过它可以作内存共享即可——打开Binder设备、执行mmap、通过Binder驱动发送请求、获取结果。
既然要做封装,那么这一层应该是封装对Binder设备的操作细节,分析代码也的确是这样。从抽象的角度来说,封装肯定是为了更简便的操作,如果把Binder的打开、执行mmap等等结果封装好,那上层只需要调用相应的接口就好了,达到封装的目的。
首先得明白,ProcessState和IPCThreadState是两个类,好像是废话,但它俩的名字起得让人“浮想联翩”,老是让我把它们当做进程、线程。我们知道,一个应用程序对应一个Linux进程,OK,知道这个就好了,更多可访问:http://blog.csdn.net/mirkerson/article/details/38128637 。ProcessState和IPCThreadState就是为了进程/线程执行Binder更加方便而作的封装。
ProcessState::ProcessState() : mDriverFD(open_driver())//打开Binder以及对Binder的相关映射操作,不细看 , mVMStart(MAP_FAILED) , mManagesContexts(false) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1) { if (mDriverFD >= 0) { // XXX Ideally, there should be a specific define for whether we // have mmap (or whether we could possibly have the kernel module // availabla). #if !defined(HAVE_WIN32_IPC) // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); close(mDriverFD); mDriverFD = -1; } #else mDriverFD = -1; #endif } LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating."); }
通过上面的分析可知,对Binder的封装果然是在ProcessState里完成,实际上是使用IPCThreadState来完成——这里直接给出结果:每个进程有一个ProcessState实例,每个线程有一个IPCThreadState实例,具体的通信由每个线程自己与Binder完成,(因为采用了单例模式)。
刚才看到了ProcessState对Binder的封装,可是这个封装还不够。因为ProcessState只是对Binder的打开、mmap等作了封装,具体数据应该怎么传入Binder,传入什么样的数据等还需要更细粒度的封装,这就是IBinder。IBinder提供了子类BpBinder和BBinder的通用部分,BpBinder和BBinder就用这个通用部分来通信。到目前为止,还是在分析通信行为,没有涉及业务。
查看BpBinder的transact实现:
status_t BpBinder::transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { // Once a binder has died, it will never come back to life. if (mAlive) { status_t status = IPCThreadState::self()->transact(//的确是调用了IPCThreadState来与Binder通信 mHandle, code, data, reply, flags); if (status == DEAD_OBJECT) mAlive = 0; return status; } return DEAD_OBJECT; }
为了不复杂化,到这里只需要知道IBinder作为基类,“制定”了Client和Server端的通信的接口,具体由BpBinder和BBinder来实现。
面向对象中一个重要的继承,就是为了将通用部分单独成为一个类(抽象类、接口),然后继承,这样子类都有基类的变量或者方法。业务层也是这个逻辑,Client和Server两端分别实现通用部分(假如是接口),再按照接口中的方法分别实现自己的逻辑,这样就简洁多了。以MediaService为例,它的通用部分是IServiceManager,Client端为BpServiceManager,Server端为BnServiceManager。一直不明白,BpServiceManager和BnServiceManager在不同进程中执行,为什么一定要设计成具有IServiceManager这样一个基类呢?其实,如果没有基类,通信也完全可以的,只不过需要更多、更复杂一点的数据来保证,对方能够明白我想要的执行动作是什么。
走到这里,通信层,业务层都已经说明完了。小结:
从上图中可以看到,实际上的类继承关系比上面论述的稍微复杂一点。这里把我两个关键点就可以把不同层给粘合在一起:
到这里,就把Binder通信的框架分析完毕。
总结来说:将通信与业务层分离,专注实现各自逻辑,并恰当的粘合在一起,成为一个完整的基于Binder的跨进程通信。
标签:android style blog http color io os 使用 ar
原文地址:http://www.cnblogs.com/littlefishxu/p/4003337.html