基于 Binder 的跨进程通信以及 Service(一):Native 层
来源:互联网 发布:计算机算法与设计 编辑:程序博客网 时间:2024/05/14 19:55
写在前面
- 本文集中讲的是 Native 层的 Binder 通信以及 Service
- Java 层的在下一篇里单讲
理论基础
1. 服务是什么?
- 可以简单的把服务理解为进程里的一个对象,它能干活,而且能被别的进程“引用”。
- 比如你搞了个进程,里面 new 了一个 MyWorker 对象,该对象可以被其他进程“引用”,并调用它的
do_work()
函数来干活。那么这个对象就是一个服务。 - 一个对象想成为“服务”的必要条件是:
- 能被其他进程引用,在 Binder 系统里,代表着它必须继承自 IBinder 类。这样它就能通过 onTransact() 跟引用它的进程通信。
- 能提供业务能力,即能干活,这说明它必须同时继承自某个业务类。这样引用它的进程就能通过 transact() 来“调用”它的业务函数。
- 如上图所示,一个 MyWorker 对象可以成为一个服务。
2. Binder 是什么?
- 把 Binder 理解为一个管道,它能连通两个进程,一个进程是 Server 端,另一个是客户端。
- Server 端里有一个 IBinder 类型的对象,客户端可以拿到一个“指向它的指针”。当然不是真的指向,而是逻辑上的。因为只有在同一个进程里,才能用一个指针指向一个对象。
- 图中的虚线表示这是逻辑上的指向。实线表示实际数据流是通过 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 类型的指针。
- 上图说明了 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 类是这样:
* 客户端使用 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)
- 如上图,asInterface() 返回一个 BpMyWorker 的实例,BpMyWorker 的 mRemote 是 sp 类型的,指向 server 对象。
- BpMyWorker 的
do_work()
其实就是mRemote->transact("DOWORK",...)
- 这一切对于客户端都是透明的,客户端只需要使用业务函数即可。
- 再次提醒,虽然 BpMyWorker, asInterface() 都是给客户端使用的,但也要由服务端的编写者来编写。客户端只负责用。
3. 回顾:客户端视角
- 客户端进程通过 ServiceManager 和 IMyWorker 类的 asInterface() 函数,能“看到”服务进程里的 IMyWorker 对象。
- 客户端感觉不到自己是在跨进程,在客户端进程看来,就像是自己 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 里。
- 将来客户端用 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>
继承即可。
- 比如我们上面例子里,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>
。
- 所以我们的 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
- 基于 Binder 的跨进程通信以及 Service(一):Native 层
- 基于 Binder 的跨进程通信以及 Service(二):Java 层
- Android Framework:Binder(5)-Native Service的跨进程调用
- 总结关于Service进程通信和跨进程通信的几种方式,AIDL,Messenger,Binder。
- android 跨进程通信的代表Binder
- Android Framework:Binder(6)-Java层Service的注册及跨进程调用
- Android 跨进程通信--Binder
- Binder(native层)
- Android跨进程通信-Binder连接池的使用
- Android跨进程通信中,对Binder的不完全理解
- Android bind service讲解以及跨进程通信
- Service的绑定中,Binder跨进程与非跨进程的区别
- 基于Message的跨进程通信
- Android中跨进程通信的IPC机制(Binder框架)
- Android中的Binder跨进程通信机制
- Android Binder跨进程通信原理分析
- Binder 跨进程通信原理浅析
- Android Binder跨进程通信原理分析
- 解决ScrollView 嵌套ListView不能刷新,加载更多问题
- JAVA-11.3-模拟斗地主洗牌、发牌、并进行排序(集合)
- CentOS 6 系统优化脚本
- 关于android进行jni调用时.so文件的兼容问题
- JAVA 集合小结
- 基于 Binder 的跨进程通信以及 Service(一):Native 层
- 文章标题
- Cookie和Session漫谈
- 2017 河南省赛感想
- Oracle PLS-00103错误
- 移动端页面撑满屏幕宽高,css设置
- 单例7种写法
- WebRTC android to android 应用笔记
- 怎样查看计算机本地安装的证书certmgr.msc