基于 Binder 的跨进程通信以及 Service(一):Native 层

来源:互联网 发布:计算机算法与设计 编辑:程序博客网 时间:2024/05/14 19:55

写在前面

  • 本文集中讲的是 Native 层的 Binder 通信以及 Service
  • Java 层的在下一篇里单讲

理论基础

1. 服务是什么?

  • 可以简单的把服务理解为进程里的一个对象,它能干活,而且能被别的进程“引用”。
  • 比如你搞了个进程,里面 new 了一个 MyWorker 对象,该对象可以被其他进程“引用”,并调用它的 do_work() 函数来干活。那么这个对象就是一个服务。
  • 一个对象想成为“服务”的必要条件是:
    1. 能被其他进程引用,在 Binder 系统里,代表着它必须继承自 IBinder 类。这样它就能通过 onTransact() 跟引用它的进程通信。
    2. 能提供业务能力,即能干活,这说明它必须同时继承自某个业务类。这样引用它的进程就能通过 transact() 来“调用”它的业务函数。

Service

  • 如上图所示,一个 MyWorker 对象可以成为一个服务。

2. Binder 是什么?

  • 把 Binder 理解为一个管道,它能连通两个进程,一个进程是 Server 端,另一个是客户端。
  • Server 端里有一个 IBinder 类型的对象,客户端可以拿到一个“指向它的指针”。当然不是真的指向,而是逻辑上的。因为只有在同一个进程里,才能用一个指针指向一个对象。

Binder

  • 图中的虚线表示这是逻辑上的指向。实线表示实际数据流是通过 Binder 传送的。
  • 当 Client 端调用了 b 的 transact() 函数时,Server 端的 onTransact() 函数会被调用,并把 Client 端的参数传进来。
  • 比如,Client 可以在 transact() 的参数里写上“DOWORK”,Server 端在 onTransact() 里看到参数“DOWORK”,则去调用自己的 do_work() 函数干活。
  • 那么现在的问题是,如何在一个 Client 进程里,弄一个指针 b,指向另一个进程(Server)的对象呢?答案就是:ServiceManager。

3. ServiceManager 是什么?

  • Android 提供了一个 ServiceManager,服务端向它注册服务,客户端从他得到服务。
  • ServiceManager 只认识 Binder,向它注册服务时,要提供 IBinder 类型的对象;向它获取服务时,返回的是 IBinder 类型的指针。

ServiceManager

  • 上图说明了 Client 进程指向 Server 进程里的对象这条虚线“指针”,是通过 ServiceManager 建立的。

实际使用:最简模型

1. 服务端注册服务

  • 注册服务使用 ServiceManager 的 addService() 函数,原型如下
status_t addService(const String16& name, const sp<IBinder>& service)
  • 注册服务需要提供2个东西:
    • 服务名称,是个字符串。
    • 指向务的指针,是 sp<IBinder> 类型
  • 比如我们注册一个自己的服务:
sp<IServiceManager>sm = defaultServiceManager(); // 得到 ServiceManager 的实例,好调用它的 addService()sm->addService("my.serv", new MyWorker());// MyWorker 同时继承自 IBinder 和 IMyWorker
  • 以上语句在 ServiceManager 里注册了一个名为 “my.serv” 的服务,该服务的提供者就是一个 MyWorker 对象。以后客户端拿到该服务,就能使用 MyWorker 对象的业务函数 do_work() 了。
  • 另外注意,我们使用了 Android 提供的 defaultServiceManager() 函数来得到一个 ServiceManager 的实例,然后调用它的 addService()。

2. 客户端得到服务

  • 得到服务使用 ServiceManager 的 getService() 函数,原型如下
sp<IBinder> getService(constString16& name)
  • 取得服务只需要提供服务名称字符串,返回值是个 IBinder 指针。
  • 比如我们得到刚才注册的那个服务:
sp<IServiceManager>sm = defaultServiceManager();sp<IBinder> binder = sm->getService("my.serv");
  • 以上语句从 ServiceManager 处得到了刚才注册的 my.serv 这个服务。它是一个 IBinder 类型的指针。

3. 客户端使用服务

  • 我们可以调用 IBinder 的 transact() 函数,向远端对象发命令:
binder->transact("DOWORK");
  • 以上语句把 “DOWORK” 发送给 Server 进程的 onTransact() 函数。”DOWORK”可以是我们自己定义的枚举类型,让 Server 端根据它选择干什么。

4. 服务端处理命令

  • 服务端收到 “DOWORK” 这个指令后,可以真正的干活了
  • 服务端重写继承自 IBinder 的 onTransact() 函数,如果客户端发来的命令是 “DOWORK” 则调用自身的 do_work():
status_t MyWorker::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){    switch(code)    {        case DOWORK:            do_work();            break;    }}

实际使用:实用模型

1. 最简模型的问题

  • 客户端调用比较麻烦,每调用一个业务函数都要用 binder->transact(xxx) 发一个命令
  • 如果涉及到返回值,参数传递等,客户端用起来就更费劲了
  • transact() 的原型如下:
status_t transact(  uint32_t code,                     const Parcel& data,                    Parcel* reply,                    uint32_t flags = 0)
  • 每次使用之前都要准备好 data 作为参数;reply 作为带出值,并且知道想调用的服务端命令对应的 code 叫什么。
  • 客户端根本不想这么使用一个远程 server

2. 实用模型

  • 客户端想要简单的使用服务:
  • 从 ServiceManger 得到 binder 之后,只要简单一步,就能把 binder 变成这样一个对象:
  • 它能直接调用业务函数,比如 do_work(),调用的本质是给 server 发命令,让 server 做 do_work()
  • 由此看来,客户端需要的是一个 IMyWorker 对象以及能把 binder 变成 IMyWorker 对象 的“简单一步”
  • 这 “简单一步” 就是 IMyWorker 的静态函数 asInterface(),其原型如下:
sp<IMyWorker> IMyWorker::asInterface(sp<IBinder>& binder)
  • 该函数以 binder 为参数,返回一个 IMyWorker 对象。
  • 客户端看到的 IMyWorker 类是这样:

IMyWorker
* 客户端使用 server 的方式变成如此简单:

sp<IServiceManager>sm = defaultServiceManager();sp<IBinder> binder = sm->getService("my.serv");sp<IMyWorker> wk = IMyWorker::asInterface(binder);wk->do_work();
  • 想让客户端变的如此轻松,关键是 asInterface() 函数的实现
  • asInterface() 一般由服务端实现,作为接口暴露给客户端使用
  • asInterface() 返回的东西很重要,它要有业务函数 do_work(),还要有一个指针 binder,指向远端服务。
  • do_work() 的实现应该是给服务端发消息,通过 binder->transact()
  • 用于让 asInterface() 返回的类一般叫做 BpMyWorker,p 是 proxy 的意思,因为这是给客户端使用的。(B 是 Binder)

BpMyWorker

  • 如上图,asInterface() 返回一个 BpMyWorker 的实例,BpMyWorker 的 mRemote 是 sp 类型的,指向 server 对象。
  • BpMyWorker 的 do_work() 其实就是 mRemote->transact("DOWORK",...)
  • 这一切对于客户端都是透明的,客户端只需要使用业务函数即可。
  • 再次提醒,虽然 BpMyWorker, asInterface() 都是给客户端使用的,但也要由服务端的编写者来编写。客户端只负责用。

3. 回顾:客户端视角

  • 客户端进程通过 ServiceManager 和 IMyWorker 类的 asInterface() 函数,能“看到”服务进程里的 IMyWorker 对象。
    ClientView
  • 客户端感觉不到自己是在跨进程,在客户端进程看来,就像是自己 new 出来一个 IMyWorker 对象一样。只不过不是 new 出来,而是从 ServiceManager 那 get 一个。
  • 小结:客户端需要做的事情很少,仅需get一个binder,转换成逻辑上的 IMyWoker “对象”,然后调用该对象的业务函数即可。
  • 所有这些功能,都是在服务端的代码里实现的。

实用模型的代码实现

  • 代码的实现完全是服务端的事情,尽管 asInterface() 函数是只有客户端才会使用的,但要由服务端提供。

1. 定义 IMyWorker 类

/* IMyWorker.h */class IMyWorker: public IInterface{public:  virtual void do_work() = 0;  virtual void stop_work() = 0;  static sp<IMyWorker> asInterface(const sp<IBinder>& obj);}

2. 用子类 MyWorker 实现业务函数

  • 子类 MyWorker 实现 IMyWorker 的业务函数 do_work(), stop_work(), 真正的干活。它同时还要继承自 IBinder,以便可以被添加到 ServiceManager 里。

MyWorker

  • 将来客户端用 getService() 得到的 binder,就指向这个 MyWorker 对象。
  • 代码如下:
/* IMyWorker.cpp */class MyWorker: publilc IMyWorker, public IBinder{}void MyWorker::do_work(){    // do work here ...}   void MyWorker::stop_work(){    // stop work here ...}   

3. 实现 static 函数 asInterface()

  • asInterface() 返回 BpMyWorker,所以首先得定义这个类
/* IMyWorker.cpp */// 定义 BpMyWorker 类,用于 asInterface() 返回class BpMyWorker: public IMyWorker{public:    // 构造函数接收一个 IBindre 指针,把它保存到成员变量 mRemote 里    BpMyWorker(sp<IBinder>& binder)    :mRemote(binder)    {    }    // 业务函数仅仅是利用 mRemote 给远端进程发送命令    vitural do_work()    {        Parcel data, reply;        mRemote->transact(WORK, data, &reply);        return;    }    // 业务函数仅仅是利用 mRemote 给远端进程发送命令          vitural stop_work()    {        Parcel data, reply;        mRemote->transact(STOP, data, &reply);        return;    }private:    sp<IBinder> mRemote; //用于指向 Server 端的对象}// 实现 asInterface()sp<IMyWorker> IMyWorker::asInterface(const sp<IBinder>& binder){    return new BpMyWorker(binder);}
  • 现在我们实现了给客户端使用的类,以及其业务函数。客户端的业务函数只负责给服务端发命令,服务端要接收并处理。

4. 服务端处理命令

  • 服务端怎么接受该命令? 同样是用 IBinder,IBinder 还有个方法叫 onTransact(),会收到 transact() 发来的命令。
  • 所以,现在服务端还要多干一件事,处理客户的命令
/* IMyWorker.cpp */class MyWorker: publilc IMyWorker, public IBinder{}void MyWorker::do_work(){    // do work here ...}   void MyWorker::stop_work(){    // stop work here ...}// 实现来自 IBinder 的 onTransact() 函数,处理客户端 transact() 发来的命令status_t MyWorker::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){    switch(code)    {        case WORK:            do_work();            break;        case STOP:            stop_work();            break;    }}

5. 写个主程序,生成进程,注册服务

  • 需要写个主程序,能运行起来,形成进程,该进程向 ServiceManager 注册服务
int main(){  sp<IServiceManager> sm =defaultServiceManager();  // 注册服务  sm->addService(“my.serv”, new MyWorker());  while(1){}}

6. 写个客户端程序,使用服务

  • 再写个测试程序,试一下使用服务的感觉
int main(){    sp<IServiceManager> sm =defaultServiceManager();    // 取得服务    sp<IBinder> binder = sm->getService(“my.serv”);    // 转为 IMyWorker 指针    sp<IMyWorker> wk = IMyWorker::asInterface(binder);    // 调用远端对象业务函数    wk->do_work();    wl->stop_work();}

Android 都准备好了

  • 上面的流程里,有很多东西是所有 Service 通用的,所以 Android 为我们准备好了一些好东西。
  • 用上这些东西,我们的代码会简短一些。

1. BpXXX

  • BpXXX 一方面肩负着指向远端进程的任务:BpMyWorker 要能指向远端进程的 MyWorker 对象,所以其必须有个成员变量 mRemote,是 IBinder* 类型的。这可以通过继承 BpRefBase 实现。
  • 另一方面肩负着调用业务函数的任务:BpMyWorker 要能调用 MyWorker 的业务函数 do_work() 等,这继承自 IMyWorker。
  • 所以说 BpXXX 一般继承自 BpRefBase 和 IXXX,Android 为我们准备好了这一切,它搞了个模板类 BpInterface<INTERFACE> ,它继承自 BpRefBase 和 INTERFACE。所以我们的 BpXXX 直接从 BpInterface<IXXX> 继承即可。

Bp

  • 比如我们上面例子里,BpMyWorker 可以直接从 BpInterface 继承。

2. interface_cast

  • 客户端从 ServiceManager 拿到 binder 之后,都会调用 IXXX::asInterface(binder)
  • 比如我们的 IMyWorker::asInterface(binder)
  • 所以 Android 为我们准备了一个模板函数interface_cast<INTERFACE>(binder)(位于 IInterface.h):
template<typename INTERFACE>inline sp<INTERFACE> interface_cast(constsp<IBinder>& obj){    return INTERFACE::asInterface(obj);}
  • 所以,只需调用 interface_cast<IMyWorker>(binder)
    得到的就是 IMyWorker::asInterface(binder)

3. asInterface

  • 服务端总要实现 IXXX 的 asInterface(binder) 函数,而且实现方式都一样:return new BpXXX(binder)
  • 比如我们的 IMyWorker 的 asInterface 的实现就是: return new BpMyWorker(binder)
  • 所以 Android 准备好了 asInterface(obj) 函数,他是由宏 IMPLEMENT_META_INTERFACE 定义的,宏展开后,相当于 new BpXXX(obj)。

4. BnXXX

  • BnXXX 与 BpXXX 相呼应,它用 onTransact() 接收 BpXXX 发来的命令。
  • BnXXX 继承自 IBinder,让别的进程可以用一个 IBinder 指向自己。(BpXXX 的 mRemote 成员变量是 IBinder* 还记得吗?)
  • 另外 BnXXX 也需要继承业务函数,让其子类可以去实现这些业务函数。
  • 同样,Android 为我们准备了模版类 BnInterface<INTERFACE>

Bn

  • 所以我们的 MyWorker 直接从 BnInterface 继承即可

再看 ServiceManager

  • 跟 ServiceManager 的交互,也是一次基于 Binder 的 IPC
  • ServiceManager 是一个远端进程,即所谓的 server 端
  • Server 向 ServiceManager 注册 Service 的过程中,Server 相当于 Client,ServiceManager 相当于 Server
  • client 向 ServiceManager 查询 Service 的过程中,client 相当于 Client,ServiceManager 相当于 Server
  • ServiceManager 的业务函数:addService(), getService()
  • defaultServiceManager() 返回的东西,看起来是一个 IServiceManager 的指针,实际上是其子类 BpServiceManager 的指针。
0 0
原创粉丝点击