Android实战技术:理解Binder机制

Android中的RPC(IPC)是由Binder组件来实现的,虽然我们使用更多的还是AIDL,并不会直接使用Binder,但是了解了它能更有效帮助理解AIDL以及理解Android本身的一些原理和机制。

Binder的架构

与其他的Android系统的组件的架构类似,Binder也是由Java层封装,JNI,libbinder和driver组成。

Binder的主要组成有三个IInterface, IBinder, Binder和BinderProxy。但是我们需要关注的仅是Binder对象和BinderProxy。其中BinderProxy是给客户端使用的,客户端通过调用其上的transact来marshalling数据,并且向底层发送消息;Binder是给服务端使用的,服务端要实现onTransact方法,以便处理客户端的方法调用请求并返回结果。IInterface接口主要是给系统使用的,用于在ServiceManager中查找对应的Service。它们之间的关系是:


关键的对象就是BinderProxy和Binder,BinderProxy是给客户端使用的对象,它的工作就是marshalling,然后调用IBinder的transact方法把信息传递出去。而Binder对象则是服务端的对象,它实现了客户端所需要的方法。

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
这里有一个实例,使用libbinder实现一个client与server的通讯。

注意:编译这个示例的方式是把这些文件放在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的实现也是以这样子的方式,具体的可以参考这篇文章
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值