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的實現也是以這樣子的方式,具體的可以參考這篇文章。