今天是2015.01.09,这一段时间是我工作中特别郁闷的一段日子,公司不景气同时也接近年过了,重新找一份工作也不是特别好的时候。这是一段非常时期,对于程序员来说,那就只能是代码中需求安慰了。这一段时间主要精力都放在看Android源码相关的东西上面了,正好看到网上关于Android创建native binder service相关的只是比较少,在这里我以一个实例来讲解关于怎么在Android下创建Native Binder Service。
首先在编译Binder相关的程序需要链接到Binder动态链接库,应用开发环境下使用ndk编程是不能链接binder动态链接库的,故此需要在源码开发环境下进行,本实例是放在Android源码目录的vender目录下面,然后创建一个NativeBinder文件夹,该文件夹的目录结构如下:
Android.mk 这个不用多说
Test.h 里面定义了本地方法的相关接口和本地接口代理BBxxxx
ITestService.cpp 里面定义了Bpxxxx和Bnxxxx::onTransact
TestService.cpp 这个就是Natavite Binder的实现
TestClient.cpp 这个就是通过Binder对Nataive Binder Service的使用了
1)Android.mk的具体实现,代码如下:
#生成binder native service的服务端
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libbinder \
liblog
LOCAL_MODULE := TestServer
LOCAL_SRC_FILES := \
TestService.cpp \
ITestService.cpp
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libbinder \
liblog
#生成binder native service的客户端
LOCAL_MODULE := TestClient
LOCAL_SRC_FILES := \
ITestService.cpp \
TestClient.cpp
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
关于Android.mk的相关语法问题,我在这里就不多做细说了,如果还不太了解的可以上百度或者谷歌去需求帮助。
2)Android Native Binder Service接口的定义,Test.h具体就是做的这个工作,下面给出代码
#ifndef TEST_H_H
#define TEST_H_H
#include
#include
#include
#include //flock's head
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace android;
#define LOGE(TAG,...) if(1) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define TAG "NativeService"
namespace android
{
class ITestService : public IInterface
{
public:
/*使用该宏,相当于添加了一些代码,后面会有详细分析*/
DECLARE_META_INTERFACE(TestService); // declare macro
virtual void test()=0;
};
enum
{
TEST = IBinder::FIRST_CALL_TRANSACTION,
};
class BnTestService: public BnInterface
{
public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,uint32_t flags = 0);
virtual void test()
{
LOGE(TAG,"Now get test\n");
}
};
}
#endif
(1)使用DECLARE_META_INTERFACE(TestService);相当于添加了下述代码:
static const android::String16 descriptor;
static android::sp asInterface(
const android::sp& obj);
virtual const android::String16& getInterfaceDescriptor() const;
ITestService ();
virtual ~ITestService();
(2)为ITestService接口的所有方法声明枚举,一个枚举值对应ITestService接口的一个方法(Test.h)
enum
{
TEST = IBinder::FIRST_CALL_TRANSACTION,
};
TEST相当于ITestService接口里的test方法,后续会为该枚举值调用test方法(Test.h)
在这里我想多说两句,如果大家手里有源码的话,可以参照Android多媒体框架的相关源码来学习这里,我这里为什么这样写也不是我自己独创,我也是参照Android源码来进行的。网上说为什么将BnTestService定义在头文件中,而不将BPTestService定义在头文件中,网上所说的是BPTestService对用户是透明的,这个我也没有弄清楚为什是透明的就不定义在这个头文件中呢,这个不在我们这里的讨论范畴之内,就不花多余的时间讨论了。
3)ITestService.cpp,该文件主要创建了BpTestService和实现了BnTestService::onTransact,不多说直接上代码:
#include "Test.h"
namespace android
{
class BpTestService:public BpInterface
{
public:
BpTestService(const sp& impl):
BpInterface(impl)
{
}
void test()
{
printf("in the get Test\n");
Parcel data, reply;
data.writeInterfaceToken(ITestService::getInterfaceDescriptor());
remote()->transact(TEST, data, &reply);
printf("send Print %d\n", reply.readInt32());
}
};
IMPLEMENT_META_INTERFACE(TestService, "android.TestServer.ITestService");
status_t BnTestService::onTransact(uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags)
{
switch (code) {
case TEST: {
printf("got the client msg\n");
CHECK_INTERFACE(ITest, data, reply);
test();
reply->writeInt32(100);
return NO_ERROR;
}
break;
default:
break;
}
return NO_ERROR;
}
}
这里我想重点说明的是,该处的IMPLEMENT_META_INTERFACE(TestService, "android.TestServer.ITestService");和DECLARE_META_INTERFACE(TestService); // declare macro是相互对应的,这个需要大家关注下。写到这里,Android下创建的Native Binder Service的工作已经进行了一大半了,接下来的只需要进行具体操作使用就OK了。
4)实现ITestService接口的方法(ITestService.cpp)
IMPLEMENT_META_INTERFACE(TestService, "android.TestServer.ITestService");
使用IMPLEMENT_META_INTERFACE(TestService, “android.TestServer.ITestService”);相当于添加了下述代码:
/*初始化用宏DECLARE_META_INTERFACE声明的静态字段descriptor*/
const android::String16 ITestService::descriptor("android.TestServer.ITestService");
/*实现用宏DECLARE_META_INTERFACE声明的方法getInterfaceDescriptor */
const android::String16&
ITestService::getInterfaceDescriptor() const {
return ITestService::descriptor;
}
/*实现用宏DECLARE_META_INTERFACE声明的方法asInterface */
android::sp ITestService::asInterface(
const android::sp& obj)
{
android::sp intr;
if (obj != NULL) {
/*在binder实体类BnInterface里会执行queryLocalInterface*/
intr = static_cast(
obj->queryLocalInterface(
ITestService::descriptor).get());
/*在binder引用类BpInterface里会创建BpInterface的子类对象*/
if (intr == NULL) {
intr = new BpTestService(obj);
}
}
return intr;
}
/*实现用宏DECLARE_META_INTERFACE声明的构造器和析构器 */
ITestService::ITestService() { }
ITestService::~ITestService() { };
5)实现Client端(TestClient.cpp)
#include "Test.h"
using namespace android;
#define TAG "TestClient"
int main()
{
sp < IServiceManager > sm = defaultServiceManager();
sp < IBinder > binder = sm->getService(String16("service.testservice"));
sp cs = interface_cast (binder);
LOGE(TAG,"Hello Native service\n");
cs->test();
return 0;
}
Service的Client通过Service Manager拿到的ITestService接口对象其实就是一个binder引用,也就是说其实是一个BpTestService对象,它也会实现ITestService接口的test方法,但是它并没有实现test的功能,而是跨进程调用Binder实体对象BnInterface的test方法。因为
涉及到跨进程调用,所以并没有直接调用,而是通过binder驱动转交给服务进程一些参数,然后就阻塞住。服务进程收到这些参数后,知道client端调用test方法,此时服务端调用test方法,执行完成后把执行结果通过binder驱动再返回给client所在进程,此时client的test方法收到结果后
再返回。
我们再实际的开发过程中并没有直接和binder驱动交互,而是通过Android为binder专门设计的一些核心库和框架来交互,这样就简化了代码编写复杂度。像BpInterface便是核心库和框架提供的一个类,我们在实现时,只需要调用BpInterface的构造器,并实现ITestService接口的test方
法即可,实现test方法时,通过remote()调用transact方法就能把数据提交至service端,servcie的返回结果会放在reply里。interface_cast(binder)实际上调用的方法是用宏DECLARE_META_INTERFACE声明的方法ITestService::asInterface,这里用了内联函数模版。
ProcessState和IPCThreadState是真正直接和binder驱动交互的类,核心库和框架层都是通过和这两个类交互才能和驱动间接交互。它们通过ioctl和binder驱动交互,ProcessState主要负责管理所有的binder引用,IPCThreadState则和binder传输数据。以后如果有时间再详细介绍这两个类。
6)实现Service端(ServiceTest.cpp)
#include "Test.h"
#define TAG "TestService"
namespace android {
class TestService: public BnTestService
{
public:
TestService();
~TestService();
void test();
};
TestService::TestService()
{
}
TestService::~TestService()
{
}
static int getCallPackageName(char *processName)
{
long pid = 0;
pid = IPCThreadState::self()->getCallingPid();
int ret;
char cmdline[256] = {0};
char buf[128] = {0};
sprintf(cmdline, "/proc/%d/cmdline", pid);
int fd =0;
fd = open(cmdline, O_RDONLY);
if (fd < 0) {
LOGE("PERSSION","open /proc/xxx/cmdline error");
return -1;
}
ret = read(fd, buf, sizeof(buf));
if (ret <= 0) {
LOGE("PERSSION","read /proc/xxx/cmdline error");
close(fd);
return -1;
}
close(fd);
LOGE("PERSSION","current pid = %d , package name = %s", getpid(), buf);
strcpy(processName,buf);
return 0;
}
void TestService::test()
{
char processName[256] = {0};
getCallPackageName(processName);
printf("hello test\n");
LOGE(TAG,"hello test from %s\n", processName);
}
}
int main()
{
sp < ProcessState > proc(ProcessState::self());
sp < IServiceManager > sm = defaultServiceManager();
sm->addService(String16("service.testservice"), new TestService());
//启动服务
LOGE(TAG,"Start testservice");
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
其中声明并实现binder实体类BnInterface是如下代码实现的,定义在Test.h文件中,定义如下:
class BnTestService: public BnInterface
{
public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,uint32_t flags = 0);
virtual void test()
{
LOGE(TAG,"Now get test\n");
}
};
BnInterface也是binder核心库和框架中的类,表示binder实体类,在onTransact函数里,会根据客户端提交的操作代码调用不同的函数,而操作代码就是2)里声明的枚举值,在本实例里是TEST,我们收到test后会调用test方法。本实例里将onTransact的实现放在下面的代码中实现:
IMPLEMENT_META_INTERFACE(TestService, "android.TestServer.ITestService");
status_t BnTestService::onTransact(uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags)
{
switch (code) {
case TEST: {
printf("got the client msg\n");
CHECK_INTERFACE(ITest, data, reply);
test();
reply->writeInt32(100);
return NO_ERROR;
}
break;
default:
break;
}
return NO_ERROR;
}
在这里的onTransact调用了test()方法。
7)最后是执行,查看结果
将TestServer和TestClient推到/system/bin,并修改权限
adb push TestServer /system/bin
adb push TestClient /system/bin
具体见如下图示
然后开两个cmd窗口测试:
服务端
客户端
最后统一奉上源码Binder实战大荟萃