Android中的RPC(IPC)是由Binder组件来实现的,虽然我们使用更多的还是AIDL,并不会直接使用Binder,但是了解了它能更有效帮助理解AIDL以及理解Android本身的一些原理和机制。
Binder的主要组成有三个IInterface, IBinder, Binder和BinderProxy。但是我们需要关注的仅是Binder对象和BinderProxy。其中BinderProxy是给客户端使用的,客户端通过调用其上的transact来marshalling数据,并且向底层发送消息;Binder是给服务端使用的,服务端要实现onTransact方法,以便处理客户端的方法调用请求并返回结果。IInterface接口主要是给系统使用的,用于在ServiceManager中查找对应的Service。它们之间的关系是:
Binder实现的是RPC,它与具体语言无关,所以理论上,基于Binder可以让Native的进程与Android Java层的应用程序通讯。最关键的,也是最麻烦点就在于客户端如何获取服务端的Service的IBinder对象,但也非不可能,要通过JNI和ClassLoader等一系列方式可以获得Java层的对象,其实Java层API的实现也是以这样子的方式,具体的可以参考这篇文章。
Binder的架构
与其他的Android系统的组件的架构类似,Binder也是由Java层封装,JNI,libbinder和driver组成。Binder的主要组成有三个IInterface, IBinder, Binder和BinderProxy。但是我们需要关注的仅是Binder对象和BinderProxy。其中BinderProxy是给客户端使用的,客户端通过调用其上的transact来marshalling数据,并且向底层发送消息;Binder是给服务端使用的,服务端要实现onTransact方法,以便处理客户端的方法调用请求并返回结果。IInterface接口主要是给系统使用的,用于在ServiceManager中查找对应的Service。它们之间的关系是:
Java层的源码:
- ./frameworks/base/core/java/android/os/IInterface.java
- ./frameworks/base/core/java/android/os/Binder.java
- ./frameworks/base/core/java/android/os/IBinder.java
- ./frameworks/base/core/java/android/os/Parcel.java
- ./frameworks/base/core/java/android/os/Parcelable.java
JNI Wrapper层:
- ./frameworks/base/core/jni/android_util_Binder.cpp
- ./frameworks/base/core/jni/android_util_Binder.h
在Native层使用Binder
除了在Java层使用Binder进行RPC外,在Native层也是可以使用的,因为Java层是依赖于libbinder的,而libbinder,它仅是一个共享库而已,所以在Native层也是可以使用的。只是libbinder并没有在NDK中公开,甚至它没有包含在NDK中,所以只能是在Android系统源码中使用libbinder。Native层使用Binder进行RPC与Java层十分类似,仅是语言语法上面的区别,原理和机制都是一样的,毕竟Java层仅是多穿上一层衣服而已。libbinder的代码:
- ./frameworks/base/libs/binder/BpBinder.cpp
- ./frameworks/base/libs/binder/Binder.cpp
- ./frameworks/base/include/binder/BpBinder.h
- ./frameworks/base/include/binder/IBinder.h
- ./frameworks/base/include/binder/BinderService.h
- ./frameworks/base/include/binder/Binder.h
- ./frameworks/base/libs/binder/IInterface.cpp
- ./frameworks/base/include/binder/IInterface.h
- ./frameworks/base/libs/binder/Parcel.cpp
- ./frameworks/base/include/binder/Parcel.h
注意:编译这个示例的方式是把这些文件放在packages/apps/里或者externals中,然后运行mm进行编译,编译好的文件在out/target/product/generic/system/bin下面。adb push到Android 模拟器,然后在adb shell中运行,需要打开二个adb shell一个运行nativeserver,另一个运行nativeclient,最好再用DDMS查看logcat,TAG是native_binder。
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := nativeclient
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := mathinterface.cpp \
client.cpp
LOCAL_SHARED_LIBRARIES := libutils libcutils libbinder
LOCAL_C_INCLUDES += frameworks/base/include system/core/include
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := nativeserver
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := mathinterface.cpp \
server.cpp
LOCAL_SHARED_LIBRARIES := libutils libcutils libbinder
LOCAL_C_INCLUDES += frameworks/base/include system/core/include
include $(BUILD_EXECUTABLE)
The interface:
/**
* Demonstrate how to us Binder in Native C++ codes.
*
* The interface describes the RPC calls.
*
* Based on BinderDemo: https://github.com/gburca/BinderDemo/blob/master/binder.cpp
*/
#ifndef _MATH_INTERFACE_H
#define _MATH_INTERFACE_H
#define LOG_TAG "native_binder"
#include <stdlib.h>
#include <binder/IBinder.h>
#include <utils/Log.h>
#include <utils/TextOutput.h>
using namespace android;
#define INFO(...) \
do { \
printf(__VA_ARGS__); \
printf("\n"); \
LOGD(__VA_ARGS__); \
} while (0)
/**
* The interface describing the RPC methods.
*
* RefBase is the base class for smart pointer.
*/
class MathInterface : public RefBase {
public:
enum {
PRINT = IBinder::FIRST_CALL_TRANSACTION,
ADD
};
virtual void print(const char *msg) = 0;
virtual int32_t add(int32_t a, int32_t b) = 0;
static const String16 DESCRIPTOR;
MathInterface();
virtual ~MathInterface();
};
#endif
/**
* Demonstrate how to us Binder in Native C++ codes.
*
* The interface describes the RPC calls.
*
* Based on BinderDemo: https://github.com/gburca/BinderDemo/blob/master/binder.cpp
*/
#include "mathinterface.h"
const String16 MathInterface::DESCRIPTOR("MathInterface");
MathInterface::MathInterface() {
INFO("MathInterface::MathInterface()");
}
MathInterface::~MathInterface() {
INFO("MathInterface::~MathInterface()");
}
Client:
/**
* Demonstrate how to us Binder in Native C++ codes.
*
* The interface describes the RPC calls.
*
* Based on BinderDemo: https://github.com/gburca/BinderDemo/blob/master/binder.cpp
*/
#include <stdlib.h>
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <utils/TextOutput.h>
#include "mathinterface.h"
using namespace android;
/**
* The proxy used for client side.
*/
class MathBinderProxy : public MathInterface {
private:
sp<IBinder> remote;
public:
MathBinderProxy(const sp<IBinder>& impl);
void print(const char *msg);
int32_t add(int32_t a, int32_t b);
};
MathBinderProxy::MathBinderProxy(const sp<IBinder>& impl) {
INFO("MathBinderProxy::MathBinderProxy()");
remote = impl;
}
void MathBinderProxy::print(const char *msg) {
Parcel data, reply;
data.writeInterfaceToken(MathInterface::DESCRIPTOR);
data.writeString16(String16(msg));
aout << "MathBinderProxy::print parcel to be sent:\n";
data.print(aout);
endl(aout);
remote->transact(MathInterface::PRINT, data, &reply, IBinder::FLAG_ONEWAY);
INFO("MathBinderProxy::print() is returned");
}
int32_t MathBinderProxy::add(int32_t a, int32_t b) {
Parcel data, reply;
data.writeInterfaceToken(MathInterface::DESCRIPTOR);
data.writeInt32(a);
data.writeInt32(b);
aout << "MathBinderProxy::add parcel to be sent:\n";
data.print(aout);
endl(aout);
remote->transact(MathInterface::ADD, data, &reply);
INFO("MathBinderProxy::add transact reply");
reply.print(aout);
endl(aout);
int32_t res;
status_t status = reply.readInt32(&res);
INFO("MathBinderProxy::add(%i, %i) = %i(status: %i)",
a, b, res, status);
return res;
}
static sp<MathInterface> getMathServer(const char *msg) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16(msg));
if (binder == NULL) {
INFO("getmath server, cannot find server '%s'", msg);
return NULL;
}
sp<MathInterface> svr = new MathBinderProxy(binder);
return svr;
}
int main(int argc, char **argv) {
INFO("we are the client");
const char *native = "MathServer";
const char *java = "JavaServerService";
sp<MathInterface> svc = getMathServer(java);
if (svc == NULL) {
INFO("failed to find service");
return -1;
}
svc->print("Hello, welcome to the world of native binder");
int32_t s = svc->add(2013, 3102);
INFO("Addition result: %i + %i = %i", 2013, 3102, s);
return 0;
}
Server:
/**
* Demonstrate how to us Binder in Native C++ codes.
*
* The interface describes the RPC calls.
*
* Based on BinderDemo: https://github.com/gburca/BinderDemo/blob/master/binder.cpp
*/
#include <stdlib.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <utils/TextOutput.h>
#include "mathinterface.h"
using namespace android;
/**
* The remote binder or the server.
*/
class MathBinder : public BBinder {
protected:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
public:
virtual void print(const char *msg);
virtual int32_t add(int32_t a, int32_t b);
};
status_t MathBinder::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
INFO("MathBinder::onTransact(%i) %i", code, flags);
/*
* Before executing actual method, check whether the RPC are from expected client.
* Client will write interface token, to identify interface to which those methods
* belong.
*/
if (!data.enforceInterface(MathInterface::DESCRIPTOR)) {
INFO("failed to check Interface, you might call wrong service, this is for '%s'",
String8(MathInterface::DESCRIPTOR).string());
return BAD_TYPE;
}
data.print(aout);
endl(aout);
switch(code) {
case MathInterface::PRINT: {
String16 msg = data.readString16();
print(String8(msg).string());
return NO_ERROR;
}
case MathInterface::ADD: {
int32_t a = data.readInt32();
int32_t b = data.readInt32();
int32_t sum = add(a, b);
INFO("MathBinder:onTransact add(%i, %i) = %i", a, b, sum);
reply->print(aout); endl(aout);
reply->writeInt32(sum);
return NO_ERROR;
}
default:
INFO("MathBinder, bad requesting code, no match found");
}
return BBinder::onTransact(code, data, reply, flags);
}
void MathBinder::print(const char *msg) {
INFO("MathBinder::print, msg is '%s'", msg);
}
int32_t MathBinder::add(int32_t a, int32_t b) {
return a + b;
}
int main(int argc, char **argv) {
INFO("We're the service");
defaultServiceManager()->addService(String16("MathServer"),
new MathBinder());
ProcessState::self()->startThreadPool();
INFO("Math server is running now");
IPCThreadState::self()->joinThreadPool();
INFO("Math server thread joined");
return 0;
}
Binder实现的是RPC,它与具体语言无关,所以理论上,基于Binder可以让Native的进程与Android Java层的应用程序通讯。最关键的,也是最麻烦点就在于客户端如何获取服务端的Service的IBinder对象,但也非不可能,要通过JNI和ClassLoader等一系列方式可以获得Java层的对象,其实Java层API的实现也是以这样子的方式,具体的可以参考这篇文章。