android binder 基础实例及解析(一)【转】-程序员宅基地

技术标签: 移动开发  

本文转载自:http://blog.csdn.net/newchenxf/article/details/49359283#insertcode

原文网址(转载请注明出处): 
http://blog.csdn.net/newchenxf/article/details/49359283

众所周知,binder是android最难啃的骨头,本人深有体会,分析framework的代码很痛苦,搞不清函数到底怎么从一个进程调用到另一个进程。 
所以,我不得不冷静一下,仔细分析binder到底怎么用,并写了一些sample code,作为实验。

网上已有各位大神对binder做深入分析,比如 
【Android深入浅出之Binder机制】 
http://www.cnblogs.com/innost/archive/2011/01/09/1931456.html 
【Binder详解】 
http://blog.csdn.net/yangzhiloveyou/article/details/14043801 
【Android Binder机制(超级详尽)】 
http://blog.csdn.net/coding_glacier/article/details/7520199 
感谢他们的分享。

本文将提供一个最基础的binder例子,并对应的做分析,慢慢拨开binder的神秘面纱。 
例子的目录结构。 
Android.mk 
client->Android.mk, main_client.cpp) 
interface->IXXXXService.cpp, IXXXXService.h 
server->Android.mk, XXXXService.cpp, XXXXService.h, main_XXXXService.cpp

a) 第一步,定义接口 IXXXXService.h

首先,要用binder,第一件事就是定义一个接口,把你想要做的事情(API)写在这个接口里。本例就2个,setSomething和getSomething。

#ifndef IXXXXService_H  
#define IXXXXService_H 

#include <binder/IInterface.h>

namespace android {
class IXXXXService : public IInterface { public: DECLARE_META_INTERFACE(XXXXService); virtual int setSomething(int a) = 0; virtual int getSomething() = 0; }; class BnXXXXService : public BnInterface<IXXXXService> { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; } #endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这个文件就写2个东西:IXXXXService和BnXXXXService。

IXXXXService,这个类必须继承IInterface,然后把你想要的API都写在这个接口里。谁会继承这个接口?有2个,一个是BpXXXXService,一个是BnXXXXService。 
BnXXXXService。这个必须继承模板类BnInterface。 
该模板的定义是

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
  • 1
  • 2

所以呢,相当于BnXXXXService继承了IXXXXService和BBinder。

奇怪的是,为啥BnInterface 需要把它写在头文件,而BpXXXXService不用呢?因为BpXXXXService对别人是透明的,客户端甚至都不需要知道它的存在。那你就要问了,不知道存在的话,我还怎么new他? 
其实android给你包好了,当你客户端调用interface_cast(*)时,就会帮你new BpXXXXService。 
但BnXXXXService就不一样,还有真正的XXXXService必须继承BnXXXXService,从而去实现真正的API函数,所以就必须放在头文件。 
没看明白没关系,后文会慢慢告诉你具体意思的。

继续,这个头文件最神秘的是DECLARE_META_INTERFACE,它定义在IInterface.h, 且看其定义。

#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface( \ const android::sp<android::IBinder>& obj); \ virtual const android::String16& getInterfaceDescriptor() const; \ I##INTERFACE(); \ virtual ~I##INTERFACE(); \
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

所以,相当于头文件加了以下的内容。

    static const android::String16 descriptor;
    static android::sp<IXXXXService> asInterface(
            const android::sp<android::IBinder>& obj); virtual const android::String16& getInterfaceDescriptor() const; IXXXXService(); virtual ~IXXXXService();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

所以,人家接口IXXXXService是很严格的喔,也有构造函数析构函数的。 
根据定义,我们可以总结,IXXXXService有如下几个函数。 
setSomething 
getSomething 
asInterface 
getInterfaceDescriptor。 
还有个静态变量descriptor,这个不管是派生类BpXXXXService还是派生类BnXXXXService,都一样的。

b) 第二步,IXXXXServie.cpp

这个文件主要实现BpXXXXService和BnXXXXService

#include "IXXXXService.h"
#include <binder/Parcel.h> #include <binder/IInterface.h> #include <utils/Log.h> #define LOG_NDEBUG 0 #define LOG_TAG "chenxf: IXXXXService" namespace android { enum { SET_SOMETHING = IBinder::FIRST_CALL_TRANSACTION, GET_SOMETHING, }; //------------------------------------proxy side-------------------------------- class BpXXXXService : public BpInterface<IXXXXService> { public: BpXXXXService(const sp<IBinder>& impl) : BpInterface<IXXXXService>(impl) { } virtual int setSomething(int a) { ALOGD(" BpXXXXService::setSomething a = %d ", a); Parcel data,reply; data.writeInt32(a); remote()->transact(SET_SOMETHING,data,&reply); return reply.readInt32(); } virtual int getSomething() { ALOGD(" BpXXXXService::getSomething "); Parcel data,reply; data.writeInterfaceToken(IXXXXService::getInterfaceDescriptor()); remote()->transact(GET_SOMETHING,data,&reply); return reply.readInt32(); } }; //---------------------- interface-------------------- IMPLEMENT_META_INTERFACE(XXXXService, "chenxf.binder.IXXXXService"); //------------------------------------server side-------------------------------- status_t BnXXXXService::onTransact ( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){ switch (code) { case SET_SOMETHING: { ALOGD("BnXXXXService::onTransact SET_SOMETHING "); reply->writeInt32(setSomething((int) data.readInt32())); return NO_ERROR; } break; case GET_SOMETHING: { ALOGD("BnXXXXService::onTransact GET_SOMETHING "); reply->writeInt32(getSomething()); return NO_ERROR; } break; } return BBinder::onTransact(code, data, reply, flags); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

就像前面的DECLARE_META_INTERFACE一样,这个文件最神秘的就是IMPLEMENT_META_INTERFACE。它其实就是实现了DECLARE_META_INTERFACE定义的虚函数,特别是asInterface! 
来看一下他的定义(IInterface.h)

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const { \ return I##INTERFACE::descriptor; \ } \ android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ const android::sp<android::IBinder>& obj) \ { \ android::sp<I##INTERFACE> intr; \ if (obj != NULL) { \ intr = static_cast<I##INTERFACE*>( \ obj->queryLocalInterface( \ I##INTERFACE::descriptor).get()); \ if (intr == NULL) { \ intr = new Bp##INTERFACE(obj); \ } \ } \ return intr; \ } \ I##INTERFACE::I##INTERFACE() { } \ I##INTERFACE::~I##INTERFACE() { } \
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

相当于IXXXXService.cpp有如下的具体内容:

    const android::String16 IXXXXService::descriptor("chenxf.binder.IXXXXService");
    const android::String16&
            IXXXXService::getInterfaceDescriptor() const {
        return IXXXXService::descriptor; } android::sp<IXXXXService> IXXXXService::asInterface( const android::sp<android::IBinder>& obj) { android::sp<IXXXXService> intr; if (obj != NULL) { intr = static_cast<IXXXXService>( obj->queryLocalInterface( IXXXXService::descriptor).get()); if (intr == NULL) { intr = new BpXXXXService(obj); } } return intr; } IXXXXService::IXXXXService() { } IXXXXService::~IXXXXService() { }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

可见,咱接口IXXXXService是有个固定的名号的,那就是“chenxf.binder.IXXXXService”。不管是派生类BpXXXXService还是派生类BnXXXXService,都可以使用这个名号!(用getInterfaceDescriptor函数就行) 
此外,函数asInterface会实现new BpXXXXService。

这个文件还实现了BnXXXXService的onTransact函数。这个函数主要就是处理proxy发来的调用。比如proxy发了想要调用服务端的setSomething函数,它发起了 
remote()->transact(SET_SOMETHING,data,&reply); 
那么,经过binder驱动,将会回调到onTransact。这个函数只负责把客户的需求处理,比如,如果是SET_SOMETHING,那就执行 
setSomething((int) data.readInt32()) 
至于setSomething要做什么,那么就不关BnXXXXService的事了,它还是有虚函数setSomething,所以可以预知肯定有别的派生类要继承并实现它。那就是传说中的XXXXService。

c) 第三步,XXXXService.h

这是XXXXService的头文件,主要就是声明XXXXService,继承BnXXXXService。

#include "../interface/IXXXXService.h"
#include <binder/BinderService.h>

namespace android {
class XXXXService : public BinderService<XXXXService>, public BnXXXXService {
public: XXXXService(); static const char* getServiceName() { return "XXXXService"; }//will be the service name virtual int setSomething(int a); virtual int getSomething(); protected: int myParam; }; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

从头文件可知,XXXXService除了要继承BnXXXXService,还要继承模板类BinderService,这主要是为了让XXXXService成为一个任何进程都可以获取的binder服务程序。 
既然要继承BinderService,那就要写一个getServiceName函数,其返回值就是该服务注册到ServiceManager的名字。

d) 第四步,XXXXService.cpp

这就要具体实现说好的setSomething和getSomething函数了。 
本例比较简单,调用过来,就加1,如果你有需要,可以做更多事情。

#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <binder/Parcel.h>
#include <binder/IPCThreadState.h> #include <utils/threads.h> #include <cutils/properties.h> #include "XXXXService.h" #define LOG_NDEBUG 0 #define LOG_TAG "chenxf: XXXXService" namespace android { XXXXService::XXXXService() { myParam = 0; } int XXXXService::setSomething(int a) { ALOGD(" XXXXService::setSomething a = %d myParam %d", a, myParam); myParam += a; return 0;//OK } int XXXXService::getSomething() { ALOGD("#XXXXService::getSomething myParam = %d", myParam); return myParam; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

完成上面四步,我们做完了binder服务的基本框架 
1. 一个标准的接口,IXXXXService; 
代理端的实现BpXXXXService。对他来说,他继承了IXXXXService接口的函数,比如setSomething,但对他来说,实现这个接口主要就是传输调用remote()->transact(….),告诉服务程序有这么一个请求调用。 
2. 服务端的中间实现BnXXXXService。对他来说,继承了模板类BnInterace,相当于继承了IXXXXService和BBinder,但不去实现IXXXXService的函数,而主要实现BBinder的onTransact函数,用于处理刚才说的remote()->transact(….)。 
3. 服务端的真正实现XXXXService。他继承了BnXXXXService和BinderService。并且会实现接口的函数,比如setSomething。

请注意,BpXXXXService实现的setSomething和XXXXService实现的setSomething没有直接关系喔,他们属于不同的进程,而且各自实现该函数。BpXXXXService的setSomething主要是传递个消息,告诉binder驱动,有这么一个事情要办,binder驱动会告诉服务进程,有人找上门了,要setSomething,麻烦XXXXService去做一下。

d) 第五步,main_XXXXService.cpp

开始服务进程,启动XXXXService,并注册到ServiceManager,让所有人都可以找到他. 
既然我们都写好了XXXXService,那总得有人new他吧?那必须的。 
我们写一个main,起一个新的进程,该进程会new XXXXService,并注册到ServiceManager。

#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <utils/Log.h> #include "XXXXService.h" #define LOG_NDEBUG 0 #define LOG_TAG "chenxf: XXXXService-main" #define EASY_START_BINDER_SERVICE 0 using namespace android; int main(int argc, char** argv) { #if EASY_START_BINDER_SERVICE XXXXService::publishAndJoinThreadPool();//使用了父类BinderService的函数 #else sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm(defaultServiceManager()); sm->addService(String16(XXXXService::getServiceName()), new XXXXService()); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); #endif return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

我干嘛要写EASY_START_BINDER_SERVICE?其实没别的意思,只是发现,BinderService的publishAndJoinThreadPool函数,相当于包含了下面的addService/startThreadPool/joinThreadPool,如果你懒得写太多东西的,直接用这个函数,就完事啦。不信请看其定义。

//BinderService
    static void publishAndJoinThreadPool(bool allowIsolated = false) {
        publish(allowIsolated);
        joinThreadPool(); } static status_t publish(bool allowIsolated = false) { sp<IServiceManager> sm(defaultServiceManager()); return sm->addService( String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);//SERVICE是模板,对本例来说,就是XXXXService } static void joinThreadPool() { sp<ProcessState> ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); IPCThreadState::self()->joinThreadPool(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

我们还是以传统写法来一步步分析吧。也就是

5.1) sp proc(ProcessState::self())

请看定义:

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) { return gProcess; } gProcess = new ProcessState; return gProcess; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

new了一个ProcessState,注意注意,gProcess是全局变量,所以,一旦这个进程main_XXXXService第一次调用了self()后,它就初始化了,后面任何地方再调用self(),拿到的也是同一个ProcessState实例,也就是说,一个进程只有一个ProcessState。 
这个ProcessState干了一个最重要的事情,那就是打开binder驱动。 
请看其构造函数。

ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false) , mThreadPoolSeq(1) { ...... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

也就是mDriverFD = open_driver()。

static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);
    if (fd >= 0) { ...... size_t maxThreads = 15; result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } } else { ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); } return fd; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

该函数很简单,就是打开binder驱动,然后设置当前进程,最多可以有15个线程使用binder。

5.2) sp sm(defaultServiceManager())

这个其实没话说啦,就是获得ServiceManager的客户端,其实本进程和ServiceManager通信,也是用binder,一样是client/server模式。

5.3) sm->addService(String16(XXXXService::getServiceName()), new XXXXService())

很明显,实例化了XXXXService,并且注册到ServiceManager,名字就是刚才在XXXXService.h强调要写的getServiceName的返回值。本例设置的名字就是“XXXXService”。别的进程要找到它,只要找ServiceManager,告诉ServiceManager需要一个名字叫“XXXXService”的服务员^_^。

5.4) ProcessState::self()->startThreadPool()

恩,ProcessState::self()就是全局变量gProcess,刚才第一步已经初始化过啦。 
所以其实第一步不做,也是可以的,只是咱为了代码清晰,还是写着吧。

void ProcessState::startThreadPool()
{
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true; spawnPooledThread(true); } } //spawnPooledThread void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { String8 name = makeBinderThreadName(); ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp<Thread> t = new PoolThread(isMain); t->run(name.string()); } } //makeBinderThreadName String8 ProcessState::makeBinderThreadName() { int32_t s = android_atomic_add(1, &mThreadPoolSeq); String8 name; name.appendFormat("Binder_%X", s); return name; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

主要就是调用了spawnPooledThread,该函数又new了一个 PoolThread类(继承了Thread),然后调用run启动了该子线程,名字是Binder_*。 
这就是为啥,我们看android服务程序的某个服务程序,比如mediaserver,经常可以看到Binder_1等子线程。

#top -t | grep mediase
  ...... 144 587 2 0% S 147108K 11936K fg media Binder_1 /system/bin/mediaserver
  • 1
  • 2
  • 3

那么,这个子线程会干嘛呢? 
我们知道,PoolThread继承了Thread,所以当执行run以后,将会threadLoop函数。 
来看PoolThread的threadLoop函数。

// define in ProcessState.cpp
virtual bool threadLoop()
{
    IPCThreadState::self()->joinThreadPool(mIsMain); return false; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

他竟然执行了和第五步一样的内容?!!!!而且这是子线程运行的,而第五步joinThreadPool是主线程执行的。没错,在文章【Android深入浅出之Binder机制】也提到过这件事,2个地方都用了joinThreadPool。其实不影响工作,我特地把第五步的“IPCThreadState::self()->joinThreadPool()”换成“while(1){sleep(1)}”,然后binder服务照样可以用。

5.5) IPCThreadState::self()->joinThreadPool()

虽然说上一步也会调用,但我们还是分析一下他吧。 
首先还是来个self()

IPCThreadState* IPCThreadState::self()
{
    if (gHaveTLS) {
restart:
        const pthread_key_t k = gTLS;
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
        if (st) return st; return new IPCThreadState; } if (gShutdown) return NULL; pthread_mutex_lock(&gTLSMutex); if (!gHaveTLS) { if (pthread_key_create(&gTLS, threadDestructor) != 0) { pthread_mutex_unlock(&gTLSMutex); return NULL; } gHaveTLS = true; } pthread_mutex_unlock(&gTLSMutex); goto restart; } //constructor of IPCThreadState IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), mMyThreadId(androidGetTid()), mStrictModePolicy(0), mLastTransactionBinderFlags(0) { pthread_setspecific(gTLS, this); clearCaller(); mIn.setDataCapacity(256); mOut.setDataCapacity(256); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

好吧,他new了一个IPCThreadState。那IPCThreadState与ProcessState啥关系? 
简单,看构造函数就知道,IPCThreadState的mProcess就是唯一的ProcessState实例。

好,继续看joinThreadPool

void IPCThreadState::joinThreadPool(bool isMain)
{
    ......
    do { processPendingDerefs(); // now get the next command to be processed, waiting if necessary result = getAndExecuteCommand(); } } while (result != -ECONNREFUSED && result != -EBADF); } //getAndExecuteCommand status_t IPCThreadState::getAndExecuteCommand() { status_t result; int32_t cmd; result = talkWithDriver(); if (result >= NO_ERROR) { size_t IN = mIn.dataAvail(); if (IN < sizeof(int32_t)) return result; cmd = mIn.readInt32(); IF_LOG_COMMANDS() { alog << "Processing top-level Command: " << getReturnString(cmd) << endl; } result = executeCommand(cmd); } return result; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

可以看到,joinThreadPool就起了个while循环,然后不断的从binder驱动读取数据(talkWithDriver),然后处理(executeCommand)。 
来看一下这个函数。

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;

    switch (cmd) {
    case BR_TRANSACTION:
    {
        binder_transaction_data tr;
        result = mIn.read(&tr, sizeof(tr));
        if (tr.target.ptr) { p<BBinder> b((BBinder*)tr.cookie); error = b->transact(tr.code, buffer, &reply, tr.flags); } ...... } return result; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

读取binder来的数据tr(类型为binder_transaction_data ),这数据包含了实际的BBinder对象,然后呢,就调用了 
BBinder的transact函数。

status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    ...... err = onTransact(code, data, reply, flags); return err; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

调用了onTransact。 
注意BBinder的onTransact是虚函数,虽然他也实现了onTransact,但真正实现的是派生类XXXXService(继承关系:XXXXService->BnXXXXService->BnInterface->BBinder)。所以呢,最后就调用了XXXXService的onTransact,也就是我们写在IXXXXService的onTransact。绕了半天,终于水落石出啦!

e) 第六步,main_client.cpp,客户端开始使用binder

#include <stdio.h>
#include "../interface/IXXXXService.h"

#define LOG_NDEBUG 0
#define LOG_TAG "chenxf: Client-main" using namespace android; sp<IXXXXService> mXXXXService; void initXXXXServiceClient() { int count = 10; if (mXXXXService == 0) { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder; do { binder = sm->getService(String16("XXXXService")); if (binder != 0) break; ALOGW("XXXXService not published, waiting..."); sleep(1); // 1 s count++; } while (count < 20); mXXXXService = interface_cast<IXXXXService>(binder); } } int main(int argc, char* argv[]) { initXXXXServiceClient(); if(mXXXXService ==NULL) { ALOGW("cannot find XXXXService"); return 0; } while(1) { mXXXXService->setSomething(1); sleep(1); ALOGD("getSomething %d", mXXXXService->getSomething()); } return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

这个是客户端程序,和前面的处在完全不同的进程喔。 
首先,要用binder,就得找ServiceManager(sp sm = defaultServiceManager();),告诉他我们需要一个名字为XXXXService的服务员( binder = sm->getService(String16(“XXXXService”));) 
这2句话看似简单,其实很深奥。

6.1 sp sm = defaultServiceManager()

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager; { AutoMutex _l(gDefaultServiceManagerLock); while (gDefaultServiceManager == NULL) { gDefaultServiceManager = interface_cast<IServiceManager>( ProcessState::self()->getContextObject(NULL)); if (gDefaultServiceManager == NULL) sleep(1); } } return gDefaultServiceManager; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这个函数,最最重要的,莫过于调用了ProcessState::self()!如上文所说,它将会new 一个ProcessState,那就意味着必然会执行open_driver,也就是打开/dev/binder驱动!所以说,客户端也是要打开binder驱动的喔。其实也可以理解,不打开,如何跟别人通信呢?只是android封装的太好了,搞得我们很难发现它打开了。

6.2) binder = sm->getService(String16(“XXXXService”)

由于服务端已经做过 
sm->addService(String16(XXXXService::getServiceName()), new XXXXService()),因此我们很容易就获得了service。 
那返回的binder是啥?他其实是BpBinder实例。它是ServiceManager返回的,这个具体内容要说,又得说一天,而本文侧重实例,所以这里先给出结论,那就是返回的是一个BpBinder()。

6.3)mXXXXService = interface_cast(binder)

interface_cast是啥?他是一个函数模板,且看定义

//IInterface.h
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj) { return INTERFACE::asInterface(obj); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

所以对于本例,就是

inline sp<IXXXXService> interface_cast(const sp<IBinder>& obj)
{
    return IXXXXService::asInterface(obj);
}
  • 1
  • 2
  • 3
  • 4

所以 
mXXXXService = IXXXXService::asInterface(binder)。 
还记得前文第二步没,在IXXXXService.cpp里,加了个宏IMPLEMENT_META_INTERFACE,该宏实现了asInterface函数。 
根据其定义,就是 
mXXXXService = new BpXXXXService(binder)。

至此,我们终于完成任务了!

总结

一个最最基本的binder程序写好了,结构还是很简单的。

我们来看一下,当客户端发起一个调用setSomething,程序会怎么走。

程序流程

首先,咱有2个进程,一个是客户端进程,main_client.cpp,一个是服务端进程,main_XXXXService.cpp。 
客户端进程: 
1. 客户端调用mXXXXService->setSomething(1) 
2. 程序走到BpXXXXService的setSomething函数。

virtual int setSomething(int a) {
    ALOGD(" BpXXXXService::setSomething a = %d ", a);
    Parcel data,reply; data.writeInt32(a); remote()->transact(SET_SOMETHING,data,&reply); return reply.readInt32(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

BpXXXXService把函数的参数,打包到Parcel对象,比如本例,就一个int数据,放到Parcel。 
remote()其实就是BpBinder实例。 
3. 程序走到BpBinder的transact函数。

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life. if (mAlive) { status_t status = IPCThreadState::self()->transact( mHandle, code, data, reply, flags); if (status == DEAD_OBJECT) mAlive = 0; return status; } return DEAD_OBJECT; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

调用了IPCThreadState::self()->transact函数。 
每个进程都有一个IPCThreadState实例,对client这个进程也一样。 
通过他的transact函数,往binder驱动写数据。完事。

服务端进程: 
1. 服务端的子线程通过执行executeCommand,不断的从binder驱动读取数据,如果发现是送给自己的,就处理数据,即执行了BBinder::transact,从而执行派生类BnXXXXService的onTransact。 
2. BnXXXXService的onTransact分析数据,发现有人要调用setSomething(即发送代号是SET_SOMETHING),于是乎,解包Parcel拿出函数参数,调用setSomething,其具体实现在XXXXService。 
3. XXXXService执行setSomething。完事。

ProcessState和IPCThreadState

前文分析ProcessState和IPCThreadState有点乱,所以我这里再总结一下他们的关系。 
每个进程只有唯一的ProcessState对象。客户端和服务端都有。 
只要进程调用了defaultServiceManager(),就一定会调用到 
ProcessState::self(),从而实例化ProcessState。

ProcessState实例来描述当前进程在binder通信时的binder状态。 
ProcessState有如下3个主要功能: 
1. 打开binder驱动。 
2. 创建一个thread,名字为PoolThread,该线程负责与binder驱动进行通信;这个线程的实现主体是一个IPCThreadState对象。 
3. 为指定的handle创建一个BpBinder对象,并管理该进程中所有的BpBinder对象。

那一个进程有几个IPCThreadState对象? 
理论上至少有一个,那就是ProcessState启动PoolThread线程是创建的。但如果用于还在主进程调用了 
IPCThreadState::self()->joinThreadPool(); 
那就有2个对象,因为查看IPCThreadState::self()的定义,就知道他是线程独立的。 
比如我们的server进程,执行了 
ProcessState::self()->startThreadPool(); 
IPCThreadState::self()->joinThreadPool(); 
那必将有2个IPCThreadState对象。 
但client进程,主进程或者没有任何子线程调用IPCThreadState::self()->joinThreadPool() 
因此只有一个IPCThreadState对象。

其实呢,咱一般就当做一个进程只有一个ProcessState和IPCThreadState吧。

IPCThreadState中有2个Parcel成员,mIn和mOut,IPCThreadState会不停的查询binder驱动中是否有数据可读,如果有将其读出并保存到mIn,同时不停的检查mOut是否有数据需要向BD发送,如果有,则将其内容写入到binder驱动中,总而言之,binder驱动中读出的数据保存到mIn,待写入到binder驱动中的数据保存在了mOut中。 
ProcessState中生成的BpBinder实例通过调用IPCThreadState的transact函数来向IPCThreadState的mOut中写入数据,这样的话这个binder IPC过程的client端的调用请求的发送过程就明了了。

下图给出ProcessState与IPCThreadState在client进程和server进程的存在方式。 
这里写图片描述
由图中可知,对于client进程,IPCThreadState是主线程创建的,运行在主线程中。主线程将会调用其transact函数,而如上文所说,transact主要调用talkWithDriver,发送数据到driver,然后获得reply,由waitForResponse处理。 
可见,对于client进程,并不需要起单独的线程不断的监听数据,毕竟,他只是client。 
对于server进程,IPCThreadState是子线程PoolThread创建的。(假设咱main_XXXXService.cpp不写IPCThreadState::self()->joinThreadPool())。 
并且,子线程通过joinThreadPool函数,起一个while循环,不断的talkWithDriver,监听driver数据,有数据后,通过executeCommand处理,如果是有效数据,调用BBinder->transact,进而调用BBidner->onTransact,最后走到派生类BnXXXXService的onTransact。

client进程与server进程的关系

说了这么多,总得给一个总框图吧。别人都是一开始给出,我就最后给出吧。^^ 
这里写图片描述

测试结果

程序都写好了,总得能跑对吧,恩,首先启动2个进程: 
root@sprint:/ # test_binder_server & 
[1] 2784 
root@sprint:/ # test_binder_client & 
[2] 2786

然后看log 
root@sprint:/ # logcat -v threadtime

10-24 06:08:40.711 2786 2786 D chenxf: IXXXXService: BpXXXXService::setSomething a = 1 10-24 06:08:40.711 2784 2784 D chenxf: IXXXXService: BnXXXXService::onTransact SET_SOMETHING 10-24 06:08:40.711 2784 2784 D chenxf: XXXXService: XXXXService::setSomething a = 1 myParam 0 10-24 06:08:41.712 2786 2786 D chenxf: IXXXXService: BpXXXXService::getSomething 10-24 06:08:41.712 2784 2785 D chenxf: IXXXXService: BnXXXXService::onTransact GET_SOMETHING 10-24 06:08:41.712 2784 2785 D chenxf: XXXXService: #XXXXService::getSomething myParam = 1 10-24 06:08:41.712 2786 2786 D chenxf: Client-main: getSomething 1 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

从log可知,BpXXXXService是活在客户端进程2786,而BnXXXXService和XXXXService是活在服务端进程2784。

附录

3个Android.mk一并贴出。 
Android.mk

include $(call all-subdir-makefiles)
  • 1

client/Android.mk

LOCAL_PATH:= $(call my-dir)
#LOCAL_CFLAGS_ALL :=-I. -I$(LOCAL_PATH)/..

include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ main_client.cpp \ ../interface/IXXXXService.cpp \ ../interface/ICallback.cpp \ Callback.cpp LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder LOCAL_C_INCLUDES := \ frameworks/base/include \ frameworks/native/include \ $(VENDOR_SDK_INCLUDES) LOCAL_MODULE:= test_binder_client LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

server/Android.mk

LOCAL_PATH:= $(call my-dir)
#LOCAL_CFLAGS_ALL :=-I. -I$(LOCAL_PATH)/..

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
        main_XXXXService.cpp \ XXXXService.cpp \ ../interface/IXXXXService.cpp \ ../interface/ICallback.cpp LOCAL_SHARED_LIBRARIES := \ libcutils libutils libbinder libdl LOCAL_C_INCLUDES := \ frameworks/base/include \ frameworks/native/include \ $(VENDOR_SDK_INCLUDES) LOCAL_MODULE:= test_binder_server LOCAL_MODULE_TAGS := optional

转载于:https://www.cnblogs.com/zzb-Dream-90Time/p/7679995.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/anfeng3664/article/details/101180231

智能推荐

hdu 1229 还是A+B(水)-程序员宅基地

文章浏览阅读122次。还是A+BTime Limit: 2000/1000 MS (Java/Others)Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 24568Accepted Submission(s): 11729Problem Description读入两个小于10000的正整数A和B,计算A+B。...

http客户端Feign——日志配置_feign 日志设置-程序员宅基地

文章浏览阅读419次。HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息。FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。BASIC:仅记录请求的方法,URL以及响应状态码和执行时间。NONE:不记录任何日志信息,这是默认值。配置Feign日志有两种方式;方式二:java代码实现。注解中声明则代表某服务。方式一:配置文件方式。_feign 日志设置

[转载]将容器管理的持久性 Bean 用于面向服务的体系结构-程序员宅基地

文章浏览阅读155次。将容器管理的持久性 Bean 用于面向服务的体系结构本文将介绍如何使用 IBM WebSphere Process Server 对容器管理的持久性 (CMP) Bean的连接和持久性逻辑加以控制,使其可以存储在非关系数据库..._javax.ejb.objectnotfoundexception: no such entity!

基础java练习题(递归)_java 递归例题-程序员宅基地

文章浏览阅读1.5k次。基础java练习题一、递归实现跳台阶从第一级跳到第n级,有多少种跳法一次可跳一级,也可跳两级。还能跳三级import java.math.BigDecimal;import java.util.Scanner;public class Main{ public static void main(String[]args){ Scanner reader=new Scanner(System.in); while(reader.hasNext()){ _java 递归例题

面向对象程序设计(荣誉)实验一 String_对存储在string数组内的所有以字符‘a’开始并以字符‘e’结尾的单词做加密处理。-程序员宅基地

文章浏览阅读1.5k次,点赞6次,收藏6次。目录1.串应用- 计算一个串的最长的真前后缀题目描述输入输出样例输入样例输出题解2.字符串替换(string)题目描述输入输出样例输入样例输出题解3.可重叠子串 (Ver. I)题目描述输入输出样例输入样例输出题解4.字符串操作(string)题目描述输入输出样例输入样例输出题解1.串应用- 计算一个串的最长的真前后缀题目描述给定一个串,如ABCDAB,则ABCDAB的真前缀有:{ A, AB,ABC, ABCD, ABCDA }ABCDAB的真后缀有:{ B, AB,DAB, CDAB, BCDAB_对存储在string数组内的所有以字符‘a’开始并以字符‘e’结尾的单词做加密处理。

算法设计与问题求解/西安交通大学本科课程MOOC/C_算法设计与问题求解西安交通大学-程序员宅基地

文章浏览阅读68次。西安交通大学/算法设计与问题求解/树与二叉树/MOOC_算法设计与问题求解西安交通大学

随便推点

[Vue warn]: Computed property “totalPrice“ was assigned to but it has no setter._computed property "totalprice" was assigned to but-程序员宅基地

文章浏览阅读1.6k次。问题:在Vue项目中出现如下错误提示:[Vue warn]: Computed property "totalPrice" was assigned to but it has no setter. (found in <Anonymous>)代码:<input v-model="totalPrice"/>原因:v-model命令,因Vue 的双向数据绑定原理 , 会自动操作 totalPrice, 对其进行set 操作而 totalPrice 作为计..._computed property "totalprice" was assigned to but it has no setter.

basic1003-我要通过!13行搞定:也许是全网最奇葩解法_basic 1003 case 1-程序员宅基地

文章浏览阅读60次。十分暴力而简洁的解决方式:读取P和T的位置并自动生成唯一正确答案,将题给测点与之对比,不一样就给我爬!_basic 1003 case 1

服务器浏览war文件,详解将Web项目War包部署到Tomcat服务器基本步骤-程序员宅基地

文章浏览阅读422次。原标题:详解将Web项目War包部署到Tomcat服务器基本步骤详解将Web项目War包部署到Tomcat服务器基本步骤1 War包War包一般是在进行Web开发时,通常是一个网站Project下的所有源码的集合,里面包含前台HTML/CSS/JS的代码,也包含Java的代码。当开发人员在自己的开发机器上调试所有代码并通过后,为了交给测试人员测试和未来进行产品发布,都需要将开发人员的源码打包成Wa..._/opt/bosssoft/war/medical-web.war/web-inf/web.xml of module medical-web.war.

python组成三位无重复数字_python组合无重复三位数的实例-程序员宅基地

文章浏览阅读3k次,点赞3次,收藏13次。# -*- coding: utf-8 -*-# 简述:这里有四个数字,分别是:1、2、3、4#提问:能组成多少个互不相同且无重复数字的三位数?各是多少?def f(n):list=[]count=0for i in range(1,n+1):for j in range(1, n+1):for k in range(1, n+1):if i!=j and j!=k and i!=k:list.a..._python求从0到9任意组合成三位数数字不能重复并输出

ElementUl中的el-table怎样吧0和1改变为男和女_elementui table 性别-程序员宅基地

文章浏览阅读1k次,点赞3次,收藏2次。<el-table-column prop="studentSex" label="性别" :formatter="sex"></el-table-column>然后就在vue的methods中写方法就OK了methods: { sex(row,index){ if(row.studentSex == 1){ return '男'; }else{ return '女'; }..._elementui table 性别

java文件操作之移动文件到指定的目录_java中怎么将pro.txt移动到design_mode_code根目录下-程序员宅基地

文章浏览阅读1.1k次。java文件操作之移动文件到指定的目录_java中怎么将pro.txt移动到design_mode_code根目录下

推荐文章

热门文章

相关标签