全部博文(24)
分类: Android平台
2015-02-09 22:03:09
Binder应用--实现自己的Service(C++实现)
下面的C++代码实现了一个自己的Service,假设我这里要实现的服务接口为MyService,这里列出几个必要的文件:
Android.mk // 编译必需
IMyService.h // 定义IMyService接口类,
IMyService.cpp // 实现IMyService接口类
MyService.cpp // 实现MyService接口类,它是具体提供服务的类,并在这里注册服务
MyServiceTest.cpp // 使用MyService服务接口例子
与在JAVA层实现一个服务类似,先给出接口定义
------------------------------ IMyService.h----------------------------------------
#ifndef I_MY_SERVICE_H__
#define I_MY_SERVICE_H__
#include
#include
#include
#include
#include
#include
#include
class IMyService : public IInterface
{
public:
DECLARE_META_INTERFACE(MyService);
virtual int doSomething(int) = 0;
enum {
DO_SOMETHING = IBinder::FIRST_CALL_TRANSACTION,
};
};
class
BnMyService : public BnInterface
{
public:
virtual status_t onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags = 0);
};
#endif
----------------------------------------------------------------------------------------
BnMyService表示服务端的本地Binder对象(n 的意思是native),一会会看到它确实是在服务端实例化的。再来看接口实现:
------------------------------ IMyService.cpp----------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace android {
#include "IMyService.h"
class BpMyService : public
BpInterface
{
public:
BpMyService(const
sp
: BpInterface< IMyService >(impl)
{
}
virtual int doSomething(int);
};
int BpMyService::doSomething(int x)
{
Parcel data, reply;
data.writeInterfaceToken(IMyService::getInterfaceDescriptor());
data.writeInt32(x);
remote()->transact(DO_SOMETHING, data, &reply);
return reply.readInt32();
}
IMPLEMENT_META_INTERFACE(MyService, "android.myservice.IMyService");
status_t BnMyService::onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags)
{
switch (code) {
case INTERFACE_TRANSACTION:
LOGI("onTransact->INTERFACE_TRANSACTION\n");
//CHECK_INTERFACE(IMyService, data, reply); // 如果不注释掉这句就跑不下去,原因下面会有分析 reply->writeString16(IMyService::getInterfaceDescriptor()); // 静态函数getInterfaceDescriptor()是在DECLARE_META_INTERFACE(MyService)当中定义,然后在IMPLEMENT_META_INTERFACE当中实现的
return NO_ERROR;
case DO_SOMETHING:
LOGI("onTransact->DO_SOMETHING\n");
CHECK_INTERFACE(IMyService, data, reply);
reply->writeInt32(doSomething(data.readInt32()));
return NO_ERROR;
}
return NO_ERROR;
};
}
----------------------------------------------------------------------------------------
BpMyService表示客户端代理(p表示代理的意思),此类在客户端使用。什么时候使用,查看IMPLEMENT_META_INTERFACE就可以看到了。BnMyService::onTransact是在服务端调用的,当客户端使用remote()->transact向服务端发消息时,此函数就会被触发。具体底层如何实现,需要研究Binder驱动代码,这里不详细介绍。总之记住一句:Binder就是通过某种方式将消息从一端转到另一端。
接下来来实现MyService,并且实现一个MyService的服务端进程,将MyService注册到Android,然后等待提供服务
------------------------------ MyService.cpp----------------------------------------
namespace android {
#include "IMyService.h"
class MyService :
public
BinderService
public BnMyService
{
public:
MyService()
{
_x = 0;
LOGI("construct MyService\n");
}
~MyService()
{
LOGI("disconstruct MyService\n");
}
// 此函数用于在注册服务时获取服务名称,在publishAndJoinThreadPool当中使用
static const char *getServiceName() { return "service.MyService"; }
// 服务端提供服务的函数
virtual int doSomething(int x)
{
int rx = _x;
LOGI("Hi doing something %d %d\n", _x, x);
_x += x;
return rx;
}
private:
int _x;
};
// 注册服务,静态函数publishAndJoinThreadPool在BinderService当中实现,BinderService类由Android提供,用于简化服务端的注册工作
void publishMyService()
{
MyService::publishAndJoinThreadPool();
}
}
using namespace android;
// 这里使用MyService
int main(int argc, const char* const argv[])
{
publishMyService();
return 0;
}
----------------------------------------------------------------------------------------
服务端做为一个单独进程存在,客户端通过IServiceManager接口获取MyService的远程引用,然后通过此引用来得到服务。
下面是客户端调用的代码
---------------------------------MyServiceTest.cpp----------------------------------------
namespace android {
#include "IMyService.h"
void callRemoteService()
{
sp
String16 serviceName("service.MyService");
getService(serviceName, &service);
int y = service->doSomething(8);
LOGI("y = %d\n", y);
}
}
using namespace android;
int main(int argc, const char* const argv[])
{
callRemoteService();
return 0;
}
----------------------------------------------------------------------------------------
getService(serviceName, &service);函数是Android提供的,内部实现其实还是通过defaultServiceManager()函数先获取远程ServiceManager引用,然后再通过ServiceManagerr的getService(...)函数来获取远程MyService的引用,代码实现请查看IServiceManager.h当中的status_t
getService(const String16& name, sp
最后给出Android.mk,其中定义了两段编译指令,先编译服务端程序,再编译客户端程序。
---------------------------------Android.mk----------------------------------------
LOCAL_PATH:= $(call my-dir)
# 编译服务端程序
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
IMyService.cpp \
MyService.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder \
libandroid_runtime
LOCAL_MODULE:= my_service
include $(BUILD_EXECUTABLE)
# 编译客户端例子程序
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
IMyService.cpp \
MyServiceTest.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder \
libandroid_runtime
LOCAL_MODULE:= my_service_app
include $(BUILD_EXECUTABLE)
----------------------------------------------------------------------------------------
编译完成后可以在板子上运行了,启动my_service时要后台运行,因为它会阻塞,如果不在后台运行的话,后面的工作就无法在终端上完成了。
1.先用service list命令查看现有的服务列表,service程序是android提供的,代码在frameworks/base/cmds/service目录下。
#service list
发现了63个服务,下面来启动我的MyService
2. 启动MyService服务进程
#./my_service &
3. 再用service list命令列出服务列表
#service list
看到变化了吗?这次发现了64个服务,而且第0个服务变成了service.MyService,先前的第0个服务现在变成第1个服务,说明我的服务已经注册成功了
我在这里还遇到一个问题,就是[]中间显示的东西,例如[android.myservice.IMyService],起初我没有实现BnMyService::onTransact当中的case INTERFACE_TRANSACTION分支,结果这个[]中间显示为空,后来通过分析service list代码发现,这个[]中间的东西是通过客户端调用服务端的INTERFACE_TRANSACTION服务获取到的,后面我加上了case INTERFACE_TRANSACTION分支实现后,[]中的东西果断显示出来了。但此过程中我又注意到在case INTERFACE_TRANSACTION分支当中调用了CHECK_INTERFACE(IMyService, data, reply);时就无法返回结果,后来查看CHECK_INTERFACE的定义和service list的代码发现是因为service list在调用service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply);之前没有调用data.writeInterfaceToken(...),这里没有调用也可以理解,因为他不知道要调用的是哪个服务。结果将data发到服务端经过CHECK_INTERFACE检查时发现此data不是针对MyService服务的,就出错返回了。这里也说明一个问题,你如果你希望case INTERFACE_TRANSACTION的东西能正确返回给service list命令,就不要调用CHECK_INTERFACE检查参数了。
4. 通过my_service_app程序调用MyService提供的服务
#./my_service_app
#logcat -s MyService
从打印看已经调用成功了,可以再来重复一个第4步看看结果是什么
我启动了MyService服务后,马上就打印了construct MyService,这说明服务端对象被创建,然后运行了两次my_service_app,第一次y = 0, 第二次y=8,这说明了服务端对象一旦创建后就一直存在。
总结:
1. IXXX仅实现接口,BpXXX表示代理,BnXXX表示服务端,分别对应三个接口类IInterface
2.服务端对象一旦注册,不会自己销毁,而且是在注册时创建,我在想是不是可以将服务端理解成一个单件?……
3.BinderService类可以简化Service类的注册过程
4.一定要多折腾,折腾多了,遇到的问题就多,遇到的问题越多理解就越深入。最近因为蓝牙的问题,还接触到一些dbus的使用,感觉dbus用起来还是不如android提供的binder来的方便。不过dbus的运用还是很广的,只是我不熟悉罢了。