Android實戰技術:理解Binder機制

來源:互聯網
上載者:User

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 := nativeclientLOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := mathinterface.cpp \client.cppLOCAL_SHARED_LIBRARIES := libutils libcutils libbinderLOCAL_C_INCLUDES += frameworks/base/include system/core/includeinclude $(BUILD_EXECUTABLE)include $(CLEAR_VARS)LOCAL_MODULE := nativeserverLOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := mathinterface.cpp \server.cppLOCAL_SHARED_LIBRARIES := libutils libcutils libbinderLOCAL_C_INCLUDES += frameworks/base/include system/core/includeinclude $(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的實現也是以這樣子的方式,具體的可以參考這篇文章。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.