Binder机制3---Native层

来源 http://blog.csdn.net/bathinbreeze/article/details/8990000

Native层流程大纲

介绍完Binder驱动的构架后,下面我们进入到Binder Native的流程分析。Binder的整体流程可分为如下几个步骤:

  1. Service Manager成为守护进程--- Service Manager告知Binder驱动程序它是Binder机制的上下文管理者。

  2. Server和Client获得Service Manager的远程接口--- defaultServiceManager接口是如何实现的。

  3. Server向SM注册服务---- IServiceManager::addService接口是如何实现的

  4. Client从SM获得服务----IServiceManager::getService接口是如何实现的

其中,第2步是3和4的基础上面4个步骤,是系统中Binder驱动和Native层的主要工作,而Binder Java层的每次调用底层函数都离不开这4步的操作。这里介绍Native层,Java层将在下章节介绍。

4.1 SM成为守护进程

SM成为守护进程的过程中,与驱动交互频繁,下面会结合之前的驱动框架来分析:

  • Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager 目录下,主要是由binder.h、binder.c和service_manager.c三个文件组成。

  • Service Manager的入口位于service_manager.c文件中的main函数Main函数主要有三个功能:

  1. 打开Binder设备文件;

  2. 调用mmap

  3. 告诉Binder驱动程序自己是Binder上下文管理者,即我们前面所说的守护进程;

  4. 进入一个无穷循环,充当Server的角色,等待Client的请求

4.1.1打开Binder设备文件

在service_manager.c打开Binder设备文件的核心代码见下图:

函数首先是执行打开Binder设备文件的操作binder_open,这个函数位于frameworks/base/cmds/servicemanager/binder.c文件中, 代码如下:


主要做了2件主要的事情:

  1. 打开驱动创建的/dev/binder文件结点,创建binder_proc结构体,保存/dev/binder上下文信息

  2. 调用驱动的mmap分配空间

4.1.2 打开设备详细分析

  1. 通过文件操作函数open打开/dev/binder设备文件,(/dev/binder是在Binder驱动模块初始化时创建的,创建入口在binder_init());此处调用驱动函数binder_open(),功能如下:

    1. threads树:用来保存binder_proc进程内用于处理用户请求的线程

    2. node树:保存binder_proc进程内的binder实体

    3. refs_by_desc和refs_by_node:保存binder_proc进程内的Binder引用(前者以句柄,后者以节点地址的key来组织)

    4. 创建struct binder_proc,保存打开设备文件的进程上下文信息到struct file的私有变量private_data中

    5. binder_proc下,会挂4棵红黑树的节点:

  2. 调用驱动的binder_mmap(),对打开的设备文件进行内存映射mmap:

    •  好处:这样,同一块物理地址分别映射给了内核和server进程,就减少了数据从内核到server的拷贝了。 (一般是:client到内核、内核到server)

    1. 通过filp->private_data得到打开/dev/binder时创建的binder_proc结构。

    2. 同一块地址,会通过struct vm_area_struct 映射用户空间server进程的使用信息;通过struct vm_struct 同时映射给内核;

    3. 检查:映射内存大小不能超过4M(此处是128*1024为128K)

    4. 调用get_vm_area()获得空间的vm_struct 空间,初始化binder_proc结构体

    5. 调用binder_update_pange_range()为虚拟地址空间分配空闲的物理页面

4.1.3 成为守护进程

回到frameworks/base/cmds/servicemanager/service_manager.c文件中的main函数,下一步 就是调用binder_become_context_manager来通知Binder驱动程序自己是Binder机制的上下文管理者,即守护进程。


binder_become_context_manager函数位于frameworks/base/cmds/servicemanager/binder.c文件中,


这里通过调用ioctl文件操作函数来通知Binder驱动程序自己是守护进程,命令号是BINDER_SET_CONTEXT_MGR,流程如下:

  1. 通过filp->private_data获得proc变量

  2. 通过binder_get_thread()获得线程信息,其流程如下:

    • 把当前线程pid作为键值,在进程proc->thread表示的红黑树中进行查找,看是否创建了binder_thread信息;如果没有创        建,创建完后会插入到proc->threads红黑树中,下次就可从proc中找到

  3.  碰到binder_context_mgr_node和binder_context_mgr_uid。由于第一次用 到,binder_context_mgr_node表示SM的实体,此时为null; binder_context_mgr_uid表示SM守护进程的id,此时为-1;于是会初始化binder_context_mgr_uid为 current->cred->euid,这样当前线程就成为Binder机制的守护进程了,并且通过binder_new_node为 Service Manager创建Binder实体。

  4. 如果第3步创建了新的binder_node,就会把新建的binder_node指针保存在binder_context_mgr_node中

4.1.4进入loop循环

回到frameworks/base/cmds/servicemanager/service_manager.c文件中的main函数中,继续往下看,会看到调用了binder_loop()函数进入循环,等待Client来请求。


binder_loop函数定义在frameworks/base/cmds/servicemanager/binder.c文件中,流程如下:

  1. 首先是通过binder_write()执行BC_ENTER_LOOPER命令告诉Binder驱动程序,Service Manager要进入循环了,调用驱动 ioctl():

    • 调用binder_get_thread函数获取binder_thread,就能从proc中直接找到了,不需要创建一个新的。这里进入case BINDER_WRITE_READ:

    1. 首先是通过copy_from_user(&bwr, ubuf, sizeof(bwr))语句把用户传递进来的参数转换成struct binder_write_read 结构,并保存在本地变量bwr中,这里可以看出bwr.write_size等于4,

    2. 于是进入binder_thread_write函数,这里我们只关注BC_ENTER_LOOPER相关的代码

    3. 在执行完BC_ENTER_LOOPER时,thread->looper值就变为BINDER_LOOPER_STATE_ENTERED了,表明当前线程进入循环状态了

    4. 回到binder_ioctl函数,由于bwr.read_size == 0,binder_thread_read函数就不会被执行了,这样,binder_ioctl的任务就完成了   

  2. 回到binder_loop函数,进入for循环,又会执行一个ioctl(bs->fd, BINDER_WRITE_READ, &bwr); 由于bwr.write_size等于0,会执行binder_thread_write函数,bwr.read_size等于32,于是进入到 binder_thread_read()。

4.1.5 SM成为守护进程-总结

至此,我们就从源代码一步一步地分析完Service Manager是如何成为Android进程间通信(IPC)机制Binder守护进程的了。总结一下,Service Manager是成为Android进程间通信(IPC)机制Binder守护进程的过程是这样的:
        1. 打开/dev/binder文件:open("/dev/binder", O_RDWR);
        2. 建立128K内存映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
        3. 通知Binder驱动程序它是守护进程:binder_become_context_manager(bs);
        4. 进入循环等待请求的到来:binder_loop(bs, svcmgr_handler);
在这个过程中,在Binder驱动程序中建立了一个struct binder_proc结构、一个struct  binder_thread结构和一个struct binder_node结构,这样,Service Manager就在Android系统的进程间通信机制Binder担负起守护进程的职责了。

4.2 Server和Client获得Service Manager远程接口

我们知道,Service Manager在Binder机制中既充当守护进程的角色,同时它也充当着Server角色,然而它又与一般的Server不一样。对于普通的 Server来说,Client如果想要获得Server的远程接口,那么必须通过Service Manager远程接口提供的getService接口来获得,这本身就是一个使用Binder机制来进行进程间通信的过程。而对于Service Manager这个Server来说,Client如果想要获得Service Manager远程接口,却不必通过进程间通信机制来获得,因为Service Manager远程接口是一个特殊的Binder引用,它的引用句柄一定是0。

获取Service Manager远程接口的函数是defaultServiceManager,这个函数声明在frameworks/base/include/binder/IServiceManager.h文件中:


4.2.1 Service Manager继承关系

那么为什么要获取远程端口呢?!是因为要实现业务以及完成通信。先来看看网上的一个关于继承关系的图示:


IServiceManager类提供的业务函数:getService(), checkService(), addService(); 

BpServiceManager类继承了BpInterface类,而BpInterface类继承了BpRefBase类。

在BpRefBase类中,有一个成员变量mRemote,它的类型是IBinder*,实现类为BpBinder,它表示一个Binder引用,引用句柄值保存在BpBinder类的mHandle成员变量中。

BpBinder类通过IPCThreadState类来和Binder驱动程序并互,而IPCThreadState又通过它的成员变量mProcess来打开/dev/binder设备文件,mProcess成员变量的类型为ProcessState。
ProcessState类打开设备/dev/binder之后,将打开文件描述符保存在mDriverFD成员变量中,以供后续使用。

4.2.2 defaultServiceManager

理清了上述的继承关系后,我们再来看看defaultServicemanager这个函数。

从这个函数可以看出:gDefaultServiceManager是单例模式,调用defaultServiceManager函数时,如果 gDefaultServiceManager已经创建,则直接返回,否则通过 interface_cast<IServiceManager> (ProcessState::self()->getContextObject(NULL))来创建一个,并保存在 gDefaultServiceManager全局变量中。
在这个过程中,有4个关键步骤:

  1. 调用ProcessStaate::self()

  2.  调用getContextObject(NULL)

  3. 使用interface_cast

  4. 使用 <IserviceManager>

下面来分析这4个步骤

4.2.2.1 ProcessState::self()

ProcessState::self()是ProcessState的静态成员函数,它的作用是返回一个全局唯一的ProcessState实例 变量,就是单例模式了,这个变量名为gProcess。如果gProcess尚未创建,就会执行创建操作,在ProcessState的构造函数中,会通 过open文件操作函数打开设备文件/dev/binder,并且返回来的设备文件描述符保存在成员变量mDriverFD中。也就是得到了一个可以和底 层打交道的ProcessState类型的gProcess对象。在addervice()的流程里,会深入分析。

4.2.2.2 getContextObject()

gProcess->getContextObject(NULL),传递的值为NULL即0,返回的值为 sp<IBinder>类型的getStrongProxyForHandle(0)。 getStrongProxyForHanlde(int32_t handle)中,handle的值是一个资源项数组中的索引值。

  • 作用:根据索引查找对应的资源,如果lookupHandleLocked发现没有对应资源项,则创建一个新的BpBinder项,填充BpBinder,并返回(handle值为0)

结果为:返回一个handle为0的Binder引用,即BpBinder;于是创建Service Manager远程接口的语句可以简化为:gDefaultServiceManager = interface_cast<IServiceManager>(new BpBinder(0)); 

4.2.2.3 interface_cast

再来看函数interface_cast<IServiceManager>的实现,它是一个模板函数,定义在framework/base/include/binder/IInterface.h文件中:


这里的《INTERFACE>是IServiceManager,于是调用了IServiceManager::asInterface函 数,那么 gDefaultServiceManager = interface_cast<IServiceManager>(new BpBinder(0)); 可以变成:gDefaultServiceManager = IServiceManager::asInterface(new BpBinder(0));

IserviceManager::asInterface是通过DECLARE_META_INTERFACE(ServiceManager) 宏在IServiceManager类中声明的,它位于framework/base/include/binder/IServiceManger.h 文件中。IServiceManager::asInterface的实现是通过 IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager")宏定义的,它位于framework/base/libs/binder /IServiceManager.cpp文件中。

4.2.2.4 IServiceManager::asInterface

最终,在IServiceManager::asInterface函数中会调用:Return intr = new BpServiceManager(obj); 这里的obj就则刚才创建的new BpBinder(0),上面的代码为:

  • intr = new BpServiceManager(new BpBinder(0));

回到defaultServiceManager函数中,最终结果为:gDefaultServiceManager = new BpServiceManager(new BpBinder(0)); 

这样,Service Manager远程接口就创建完成了,它本质上是一个BpServiceManager,包含了一个句柄值为0的Binder引用。这里,实现asInterface函数,是利用BpBinder对象新建了一个BpServiceManager对象

4.3 Server向SM注册服务

实际上,BnMediaPlayerService并不是直接接收到Client处发送过来的请求,而是使用了IPCThreadState接收 Client处发送过来的请求,而IPCThreadState又借助了ProcessState类来与Binder驱动程序交互。
IPCThreadState接收到了Client处的请求后,就会调用BBinder类的transact函数,并传入相关参数,BBinder类的 transact函数最终调用BnMediaPlayerService类的onTransact函数,于是,就开始真正地处理Client的请求了。

以MediaServer作为Server的例子,这里我们只看MediaPlayerService:多媒体系统的重要服务。MS的入口函数在 \frameworks\av\media\mediaserver\ main_mediaserver.cpp 如下图,


可分为5步:

  1. 创建ProcessState

  2. 创建IServiceManager对象

  3. 初始化MediaPlayerService服务对象

  4. 创建进程的线程池 

  5. 把自己加入线程池

下面来分别分析。

4.3.1  ProcessState

之前分析过,这里详细介绍下参数传递的流程。ProcessState::self()调用创建一个ProcessState实例。 ProcessState::self()是ProcessState类的一个静态成员变量,定义在frameworks/base/libs /binder/ProcessState.cpp文件中:

这里可以看出,这个函数作用是返回一个全局唯一的ProcessState实例gProcess。全局唯一实例变量gProcess定义在frameworks/base/libs/binder/Static.cpp文件中


再来看ProcessState的构造函数:ProcessState::ProcessState()的流程:

  1. 通过open_driver()打开binder设备 /dev/binder,并将打开设备文件描述符保存在成员变量mDriverFD中,这样,与Binder驱动有了交互的通道。open_driver 这个函数同样位于frameworks/base/libs/binder/ProcessState.cpp文件中,流程如下:

    1. 通过调用驱动open()打开/dev/binder,在打开/dev/binder设备文件后,Binder驱动程序就为 MediaPlayerService进程创建了一个struct binder_proc结构体实例来维护MediaPlayerService进程上下文相关信息。

    2. 调用ioctl()执行BINDER_VERSION获得当前Binder驱动程序的版本号。将BINDER_CURRENT_PROTOCOL_VERSION写入到传入的参数arg指向的用户缓冲区中。

    3. 调 用ioctl()执行BINDER_SET_MAX_THREADS,通知Binder驱动程序最多能设置多少个线 程,MediaPlayerService最多可同时启动15个线程来处理Client端的请求。把用户传进来的参数保存在 proc->max_threads中就完毕了。(注意,这里再调用binder_get_thread函数的时候,就可以在 proc->threads中找到当前线程对应的struct binder_thread结构了,因为前面已经创建好并保存在proc->threads红黑树中)

  2. 对返回的fd使用mmap,把设备文件/dev/binder映射到内存中(流程见“成为守护进程mmap"部分)

这样,ProcessState全局唯一变量gProcess就创建完毕了,回到了frameworks/base/media/mediaserver/main_mediaserver.cpp文件中的main函数

4.3.2 创建IServiceManager对象

DefaultServiceManager函数的实现在之前分析过,则:sp<IServiceManager> sm = defaultServiceManager(); 为:sp<IServiceManager> sm = new BpServiceManager(new BpBinder(0)); 

4.3.3 注册MediaPlayerService


MediaPlayerService::instantiate()调 用:defaultServiceManager()->addService()。这个函数最终会通过 IPCthreadState->transact()和Binder驱动打交道,把数据传送到SM中,完成MediaPlayerService 的注册。defaultServiceManager()返回的对象是BpServiceManager,而BpServiceManager是 IServiceManager的后代。IServiceManager是一个抽象类,里面有我们操作业务层的各种函数。

4.3.3.1 MediaPlayerService::instantiate()

MediaPlayerService::instantiate();的实现如下:


这里,实际上是调用的BpServiceManger::addService()这个函数实现在frameworks/base/libs /binder/IServiceManager.cpp文件中的addService()。addService是一个业务层的函数,调用 remote()->transact();remote()返回的是mRemote,也就是BpBinder对象。在addService函数 中,把请求数据打包成data后,传递给了BpBinder的transact函数,把通信任务交给了BpBinder;即交给了通信层去处理。

4.3.3.2 transact()通信层的工作

BpBinder::transact()调用了:status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
这里的mHandle为0,code为ADD_SERVICE_TRANSACTION。ADD_SERVICE_TRANSACTION是上面以参数形 式传进来的,这里表示的是Service Manager远程接口,mHandle的句柄值一定是0。即,把transact工作交给了IPCThreadState。 IPCThreadState::self()中,TLS: Thread Local Storage,每个线程都有,且不共享。通过pthread_getspecific/pthread_setspecific函数可以获得/设置这些 空间中的内容。

流程如下:

  1. pthread_getspecific()从线程本地存储空间中获得保存在其中的IPCThreadState对象

  2. new一个IPCThreadState对象,调用构造函数,构造函数中会调用pthread_setspecific函数。则上面的调用变为: new IPCThreadState()->transact(mHandle, code, data, reply, flags);

    • err = writeTransactionData(BC_TRANSACTION,...); 函数里会创建一个binder_transaction_data tr,这个就是等下要传给Binder驱动的。发送的消息码为:BC_XX

    • err = waitForResponse(reply); binder设备向应用程序回复消息,BR_XX为回复的消息码

    • 其中函数中定义的binder_transaction_data是和binder设备通信的数据结构 binder_transaction_data tr;

    • handle的值传递给了target用来标识目的端,其中0为SM的标志。tr.target.handle = handle;

    • cmd:发送的命令消息码  BC_XXX,此处为BC_TRANSACTION

    • code: 用来switch/case的消息码

    • 把命令写到mOut中,并不直接发出去。mOut.writeInt32(cmd); mOut.write(&tr, sizeof(tr));

    • 该函数原型为:IPCTheadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)

    • 这时,已经把addService的请求信息”tr”写到mOut中了,写入的内容为:


    • 写流程:

    • 读流程:

    • 其中定义了binder_write_read bwr; 一个与binder设备交换数据的结构;这里调用了驱动的ioctl()与驱动交互:

    • executeCommand(int32_t cmd)收到cmd后,switch(cmd):

    • sp<BBinder>b((BBinder*)tr.cookie); 设置BnServiceXXX的对象b,BnServiceXXX是从BBinder派生

    • b->transact(); 调用transact函数,发送binder_transaction_data和Parcel类型的数据

    • 收到Binder驱动发来的service死掉的消息,只有Bp端能收到

    • 收到来自驱动的指示以创建一个新线程,用于和Binder通信

    • 接收回复的数据waitForResponse(Rarcel *reply, status_t *acquireResult)。在无限循环中(数据读完或异常,则退出)做了如下事情:

    • 构造函数 IPCThreadState()的流程:

    • IPCThreadState::transact()调用:

    1. 调用talkWithDriver();

    2. 把数据读到mIn中

    3. 把命令(BR_XXX)读出并switch/case

    4. executeCommand(cmd)执行命令

    5. 请求命令填充

    6. 调用ioctl,由binder驱动实现具体操作

    7. 接收数据缓冲区信息的填充,如果以后收到数据,就填到mIn中

    8. 调用ioctl,由binder驱动实现具体操作

    9. case BR_ERROR:

    10. case BR_TRANSACTION:

    11. case BR_DEAD_BINDER:

    12. case BR_SPAWN_LOOPER:

    13. pthread_setspecific() 把自己设置到线程本地存储中

    14. 设置mIn和mOut搜法命令的缓冲区

4.3.4 StartThreadPool()

StartThreadPool()调用spawnPooledThread()函数:sp<Thread> t = new PoolThread(isMain); isMain参数是true; PoolThread是在IPCThreadState中定义了一个Thread子类。PoolThread类中,调用threadLoop()。 threadLoop()中,在这个线程中又创建了一个IPCThreadState: IPCThreadState::self()->joinThreadPool(mIsMain)。

joinThreadPool()流程:

  1. mOut.writeInt32(isMain?BC_ENTER_LOOPER: BC_REGISTER_LOOPER); 如果isMain为true,则需要循环处理。把请求写到mOut中,过会一起发出

  2. 处理已经死亡的BBinder对象

  3. 发送命令,读取请求:调用talkWithDriver();

4.3.5 MS中的joinThreadPool()

MediaServer进程一共注册了4个服务。MS中目前有两个线程在talkWithDriver():

  • 一个是StartThreadPool启动新线程通过joinThreadPool,调用talkWithDriver(),读取binder设备;

  • 另一个是MS主线程调用joinThreadPool,调用talkWithDriver(),读取binder设备

BnService和BpService负责业务的交互;BBinder和BpBinder负责通信的交互


4.3.6 MediaServer注册服务到SM流程图


4.4 Client从SM获得服务

client从SM获得服务,也就是从SM获得 BpMediaPlayerService。BpMediaPlayerService的构造函数有一个参数impl,它的类型为const sp<IBinder>&,从上面的描述中,这个实际上就是一个BpBinder对象。 这样,要创建一个BpMediaPlayerService对象,首先就要有一个BpBinder对象。再来看BpBinder类的构造函数,它有一个参 数handle,类型为int32_t,这个参数的意义就是请求MediaPlayerService这个远程接口的进程对 MediaPlayerService这个Binder实体的引用了。因此,获取MediaPlayerService这个远程接口的本质问题就变为从 Service Manager中获得MediaPlayerService的一个句柄了。

4.4.1 详细分析:

这里我们以MediaServer中的MediaPlayService与Client交互为例,来了解请求数据是如何从通信层传递到业务层并进行处理的:

client想要获得某个Service的信息,就得先和SM交互,通过调用getService()来获得对应的Service信息,这里用到的是IMediaDeathNotifier::getMeidaPlayerService()

  1. 通过defaultServiceManager()获得SM的远程接口BpServiceManager的IServiceManager接口

  2. 用while循环通过binder= sm->getService()不断尝试获得名字为”media.player”的service;向SM查询对应服务的信息,返回一个与MediaPlayerService通信的BpBinder;如果SM上还没有注册对应的服务,则睡0.5秒再尝试,直到对应服务注册到SM上才中止

  3. 通过interface_cast,将这个binder转换成一个BpMediaPlayerService

有了BpMediaPlayerService,client就能够使用任何IMediaPlayerService提供的业务逻辑函数 了,e.g: createMediaRecorder和createMetadataRetriever等。调用这些函数都将把请求数据打包发给Binder驱动,并 由BpBinder中handle知道对应端的处理者来处理,包括

  1. 通信层接收到请求

  2. 递交给业务层处理

4.4.2 BpServiceManager::getService()

BpServiceManager::getService通过BpServiceManager::checkService执行操作。

在BpServiceManager::checkService中:

  1. 首先是通过Parcel::writeInterfaceToken往data写入一个RPC头;就是写往data里面写入了一个整数和一个字符 串“android.os.IServiceManager”, Service Manager来处理CHECK_SERVICE_TRANSACTION请求之前,会先验证一下这个RPC头,看看是否正确。接着再往data写入一个 字符串name,这里就是“media.player”了

  2. 调用remote()返回的是一个BpBinder,于是,就进行到 BpBinder::transact函数了。这里的mHandle = 0,code = CHECK_SERVICE_TRANSACTION,flags = 0。进入到IPCThread::transact函数中:

    1. 首先是调用函数writeTransactionData写入将要传输的数据到IPCThreadState的成员变量mOut中去

    2. 调 用waitForResponse(reply),这个函数通过IPCThreadState::talkWithDriver与驱动程序进行交互,这里 的needRead为true,因此,bwr.read_size大于0;outAvail也大于0,因此,bwr.write_size也大于0;

    3. 最后ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

4.5 c/s Native层通信小结

  • 通信层

    1. 首先server和client都调用了ProcessState的构造函数从而都向驱动申请了一块物理内存用于通信数据的存放

    2. server 调用SM的addService函数传递一个字符串和实际Binder对象在自己虚拟地址空间的地址到Binder驱动,Binder驱动记录下该地址 值,在SM申请的物理内存中分配一个虚拟地址并连同字符串一起传递给SM,而且Binder驱动会记录下两个地址之间的对应关系.

    3. client 调用SM的getService函数传递一个字符串给SM,SM将相对应的虚拟地址值传递给Binder驱动,Binder驱动查询出实际对象的地址,在 client申请的物理内存中分配一个虚拟地址并传递给client,而且Binder驱动会记录下这个地址和实际Binder对象地址之间的对应关 系,client这里得到的就是实际Binder的引用了。到了这一步,真正的Binder对象就拥有两个引用,一个在SM,一个client.

    4. client通过得到的Binder引用调用server中的函数,驱动会根据传递过来引用值找到应该处理该请求的进程,并唤醒其中的Binder线程,调用BBinder对象的onTransaction函数,最终调用到实际的函数

  • 从通信层到业务层

    • 从bpxxxService的构造函数可以看出,在Client进程,每一个BpxxxService对象都封装了一个BpBinder对象,因 此,确切来说,Client进程缓存的是BpBinder对象,每一个BpBinder对象都与一个Binder句柄值对应。每当Client进程需要创 建一个BpxxxService对象时,就会检查本地缓存中是否已经存在对应的BpBinder对象。如果存在的话,就可以直接使用它来创建一个 BpxxxService对象,否则的话,就要通过Binder驱动程序来获得一个Binder句柄值,再以这个句柄值来创建一个BpBinder对象, 最后再根据这个BpBinder对象来创建一个BpxxxService对象。

  • 业务层

    • 由于BpMediaPlayerService继承了IMediaPlayerService,IXXService有比如createMediaRecord(), CreateMetaDataRetrive()等业务层方法,则可以完成相应业务需要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值