终于还是得写一篇关于Binder的文章了。从最初接触Android到花大把时间研究Android源码,Binder一直是分析道路的拦路虎。看了几本最流行的Android源码分析书籍,每次基本上都不能把Binder相关知识看完、读透。好在一直没有放弃,第一次理解不了就跳过,下一次重新读,每次读都有新的收获。现在是时候整理整理了。

  我理解的Binder是什么?一种IPC(跨进程通信)的实现方式。注意“跨进程”,表明数据从一个进程“流向”了另一个进程。首先要了解为什么跨进程那么难?因为应用程序的地址空间都是虚地址,通过映射到物理内存空间。假如应用程序都直接在物理内存空间操作,那应用程序跨进程就非常方便了,直接传递物理地址就好了。但是,不同应用程序使用的是通过内存管理器作映射的虚拟内存空间,至于映射到哪个物理内存区域,这是未知的,从而使得跨进程传递数据变得不那么方便,但也不是没有办法。Linux中典型的管道、消息队列、共享内存等都可以实现跨进程通信。Android采用了Binder方式,自然有它的优势,所谓“灵活、方便”,这个优势还在慢慢体会中。

  要深刻理解android的Binder机制,有几点是需要理解的:

  1. android操作系统内部,可以看成一种基于C/S架构的服务提供机制。由Client发出某种服务请求,由Service执行服务并返回结果。比如一个APP需要使用照相机就需要向MediaService提出服务请求,而这两个进程之间的通信,就是通过Binder来完成,android中存在大量的这种C/S结构。
  2. 进程与类的区别:一个进程可以提供多种服务(可以通过多线程的方式,也可以是逻辑判断提供哪种服务),比如MediaService是android中的一个进程,提供了AudioFlinger服务、AudioPolicyService服务、MediaPlayerService服务等。在分析的过程中,要时刻明白当前进程执行的代码是什么。
  3. 通信与业务要区分开来:Binder说到底是一种通信方式,那么谁使用它呢?那肯定是上层代码。这里说是“上层代码”,是因为代码框架的层次结构是分析重点。这些上层代码可能在不同进程中执行。

如果看过Service实现,或者各种类的继承关系,它们是有一定的层次关系的。现在采用“从下往上”的逐层分析,在看源码时,个人发现“从下往上”往往可以取得就好的效果。结合《深入理解android 卷1》第六章来分析。

  • Binder是一个“设备”

  有设备就离不开设备驱动,Binder实际上就是一个包含驱动、协议的伪设备,通过常用的对设备的操作,如open、ioctl、mmap等,可以操作Binder这个“设备”。其中关键点为:通过mmap可以把一块虚拟地址与物理地址映射起来,这时候,A进程与Binder共享同一块物理内存,如果另一个B进程也通过Binder共享了这块内存,那么A、B两进程就共享了同一块内存——这就是不同应用程序共享内存的(通过Binder)的原理。还有有一句话就是,如果到这里,两个进程之间已经完全可以通过这个Binder来通信了,当然实现细节肯定很麻烦。故要做封装!!根据上面所说的第3点,这里仍然在描述通信细节,还没有涉及到业务。Binder驱动本身比较复杂,不作分析。可以简单化它:通过它可以作内存共享即可——打开Binder设备、执行mmap、通过Binder驱动发送请求、获取结果。

  • ProcessState和IPCThreadState

  既然要做封装,那么这一层应该是封装对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完成,(因为采用了单例模式)。

  • IBinder、BpBinder和BBinder

  刚才看到了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这样一个基类呢?其实,如果没有基类,通信也完全可以的,只不过需要更多、更复杂一点的数据来保证,对方能够明白我想要的执行动作是什么。

走到这里,通信层,业务层都已经说明完了。小结:

  1. 类名中带有“Binder”的类是为了通信而实现的
  2. 业务层利用通信层实施通信
  • 通信层与业务层的粘合

从上图中可以看到,实际上的类继承关系比上面论述的稍微复杂一点。这里把我两个关键点就可以把不同层给粘合在一起:

  1. BnServiceManager直接继承了BBinder,表示BnServiceManager可以直接参与Binder通信。
  2. BpServiceManager所继承的BpRefBase有一个mRemote的IBinder类型变量,在实际代码中会指向BpBinder,这样BpServiceManager就可以通过BpBinder来与BBinder通信了。

到这里,就把Binder通信的框架分析完毕。

总结来说:将通信与业务层分离,专注实现各自逻辑,并恰当的粘合在一起,成为一个完整的基于Binder的跨进程通信。

android分析之Binder 01的更多相关文章

  1. android分析之Binder 02

    分析Java层的ServiceManager,看看Binder在Java层是如何实现的. public final class ServiceManager { private static fina ...

  2. Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6642463 在前面几篇文章中,我们详细介绍了A ...

  3. Android深入浅出之Binder机制(转)

    Android深入浅出之Binder机制 一 说明 Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的.所以搞明白B ...

  4. Android IPC机制—Binder的工作机制

    进程和线程的关系 IPC机制即为跨进程通信,是inter-Process Communication的缩写.是指两个进程之间进行通信.在说进程通信之前,我们的弄明白什么是线程,什么是进程.进程和线程是 ...

  5. 从AIDL开始谈Android进程间Binder通信机制

    转自: http://tech.cnnetsec.com/585.html 本文首先概述了Android的进程间通信的Binder机制,然后结合一个AIDL的例子,对Binder机制进行了解析. 概述 ...

  6. [Android进阶]Binder学习(初始篇)

    Android中Binder学习(初始篇) 本篇博客学习自侯亮的博客.地址为: 红茶一杯话Binder 1 什么是Binder? 简单地说.Binder是Android平台上的一种跨进程交互技术. 该 ...

  7. 069 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 01 综合案例-数组移位-案例需求

    069 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 01 综合案例-数组移位-案例需求 本文知识点:综合案例-数组移位-案例需求 说明:因为时间紧张,本人写博客 ...

  8. 068 01 Android 零基础入门 01 Java基础语法 08 Java方法 06 参数传递问题——基本数据类型传值

    068 01 Android 零基础入门 01 Java基础语法 08 Java方法 06 参数传递问题--基本数据类型传值 本文知识点:参数传递问题--基本数据类型传值 说明:因为时间紧张,本人写博 ...

  9. 060 01 Android 零基础入门 01 Java基础语法 06 Java一维数组 07 冒泡排序

    060 01 Android 零基础入门 01 Java基础语法 06 Java一维数组 07 冒泡排序 本文知识点:冒泡排序 冒泡排序 实际案例分析冒泡排序流程 第1轮比较: 第1轮比较的结果:把最 ...

随机推荐

  1. next v5升级到next v7需要注意的地方

    title: next v5升级到next v7需要注意的地方 date: 2020-03-04 categories: web tags: [hexo,next] 大部分的设置都是一样的,但有一些细 ...

  2. oslab oranges 一个操作系统的实现 final

    见 github  https://github.com/TouwaErioH/subjects/tree/master/oslab-oranges

  3. 使用SQL-Server分区表功能提高数据库的读写性能

    首先祝大家新年快乐,身体健康,万事如意. 一般来说一个系统最先出现瓶颈的点很可能是数据库.比如我们的生产系统并发量很高在跑一段时间后,数据库中某些表的数据量会越来越大.海量的数据会严重影响数据库的读写 ...

  4. ES6 version repeatify

    ES6 version repeatify String.prototype.repeatify = String.prototype.repeatify || function (times = 1 ...

  5. TypeScript keyof typeof All In one

    TypeScript keyof typeof All In one keyof typeof refs https://www.typescriptlang.org/docs/handbook/re ...

  6. 23 种设计模式 APP & 23 Design Patterns App

    23 种设计模式 APP & 23 Design Patterns App https://github.com/xgqfrms/23-design-patterns-app https:// ...

  7. 阅文集团 招聘官网 bug

    阅文集团 招聘官网 bug https://join.yuewen.com/ 前端开发 zxx.jpg 张鑫旭 https://qidian.gtimg.com/yuewen/join/css/ima ...

  8. windwos创建和删除服务

    创建 >sc create <service name> type=kernel binpath="C:\hsys.sys" 删除 win+r 输出 regedi ...

  9. flatbuffer与protobuf对比

    在内存空间占用这个指标上,FlatBuffers占用的内存空间比protobuf多了两倍.序列化时二者的cpu计算时间FB比PB快了3000ms左右,反序列化时二者的cpu计算时间FB比PB快了900 ...

  10. 开源OA办公系统的“应用市场”,能够为协同办公开拓什么样的“前路”?

    在我们的日常生活中,应用市场这个词,总是与智能手机划上等号,不管使用的是iPhone还是安卓,总会接触到手机上的APP应用市场,我们可以在应用市场中,选择自己所需要的APP应用软件,下载使用后,可以让 ...