1. 程式人生 > >Android利用Binder進行通訊

Android利用Binder進行通訊

Android利用Binder進行通訊


Binder作為Android使用最廣泛的IPC通訊機制之一,其重要性不言而喻。Binder的實現思想與原理各路大神已經分析的十分透徹了,個人覺得最好以及最詳細的是老羅的Android之旅系列裡面關於Binder的講解:

[ Android程序間通訊(IPC)機制Binder簡要介紹和學習計劃]

Binder作為一種高效的IPC通訊手段,其使用也十分的簡單,本文參考Android MediaPlay的架構實現了一個簡單的server與client通訊Demo,具體程式碼結構如下:
這裡寫圖片描述

程式碼結構:

├── AndroidClient
│   ├── AndroidManifest.xml
│   ├── Android.mk
│   ├── jni
│   │   ├── Android.mk
│   │   ├── com_test_androidclient_MainActivity.cpp
│   │   └── com_test_androidclient_MainActivity.h
│   ├── proguard-project.txt
│   ├── project.properties
│   ├── res
│   │   ├── drawable-hdpi
│   │   │   └── ic_launcher.png
│   │   ├── drawable-ldpi
│   │   ├── drawable-mdpi
│   │   │   └── ic_launcher.png
│   │   ├── drawable-xhdpi
│   │   │   └── ic_launcher.png
│   │   ├── layout
│   │   │   └── activity_main.xml
│   │   ├── menu
│   │   │   └── main.xml
│   │   ├── values
│   │   │   ├── dimens.xml
│   │   │   ├── strings.xml
│   │   │   └── styles.xml
│   │   ├── values-v11
│   │   │   └── styles.xml
│   │   ├── values-v14
│   │   │   └── styles.xml
│   │   └── values-w820dp
│   │       └── dimens.xml
│   └── src
│       └── com
│           └── test
│               └── androidclient
│                   └── MainActivity.java
├── Android.mk
├── client
│   ├── MyClient.cpp
│   └── test.cpp
├── include
│   ├── IMyService.h
│   ├── MyClient.h
│   └── MyService.h
└── server
    ├── IMyService.cpp
    ├── main_myserver.cpp
    └── MyService.cpp

IMyService.h中宣告Service端BnMyService與Client端代理BpMyService以及三個函式

#ifndef ANDROID_IMYSERVICE_H
#define ANDROID_IMYSERVICE_H
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <stdint.h>
#include <utils/String8.h>

namespace android {

class
IMyService: public IInterface { public: DECLARE_META_INTERFACE (MyService); virtual String8 getStr() = 0; virtual void setStr(String8 str) = 0; virtual int32_t doAdd(int32_t a, int32_t b) = 0; }; //class IMyService class BpMyService: public BpInterface<IMyService> { public: BpMyService(const
sp<IBinder>& impl): BpInterface<IMyService>(impl) {} virtual String8 getStr(); virtual void setStr(String8 status); virtual int32_t doAdd(int32_t a, int32_t b); }; //class BpMyService class BnMyService: public BnInterface<IMyService> { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; //class BnMyService }; //namespace android #endif

MyService.h,MyService繼承自BnMyService

#ifndef ANDROID_MyService_H
#define ANDROID_MyService_H
#include <utils/Errors.h>  // for status_t
#include "IMyService.h"
namespace android {

class MyService: public BnMyService {
public:
    static void instantiate();
    virtual String8 getStr();
    virtual void setStr(String8 status);
    virtual int32_t doAdd(int32_t a, int32_t b);
private:
        static String8 mStr;
    MyService();
    virtual ~MyService();

};  //class MyService

};  //namespace android

#endif

MyClient.h中包含一個MyService端代理gMyService,由binder經過interface_cast轉化而成,實際是一個BpMyService的remote物件

#ifndef ANDROID_MYCLIENT_H_
#define ANDROID_MYCLIENT_H_
#include <utils/threads.h>
#include "IMyService.h"
namespace android {
class MyClient : public RefBase
{
public:
    static String8 getStr();
    static void setStr(String8 str);
    static int32_t doAdd(int32_t a, int32_t b);
private:
    class DeathNotifier: public IBinder::DeathRecipient {
        public:
            DeathNotifier() {}
            virtual ~DeathNotifier();
            virtual void binderDied(const wp<IBinder>& who);
    };
private:
    static Mutex gMutex;
    static sp<DeathNotifier> gDeathNotifier;
    static sp<IMyService> gMyService;
    static const sp<IMyService>& getMyService();
};

};  //namespace android
#endif

服務端與客戶端通訊,實際上是BnMyService與BpMyService經由binder進行通訊。
IMyService.cpp

#define LOG_TAG "IMyService"
#include <utils/Log.h>
#include <string.h>
#include "IMyService.h"

namespace android {

enum {
    GET_STR = IBinder::FIRST_CALL_TRANSACTION,
    SET_STR,
    DO_ADD,
};

String8 BpMyService::getStr()
{
    Parcel data, reply;
    data.writeInterfaceToken(IMyService::getInterfaceDescriptor());
    status_t status = remote()->transact(GET_STR, data, &reply);
    String8 str;
    if (status == NO_ERROR) {
        str = reply.readString8();
    }
    return str;
}

void BpMyService::setStr(String8 str)
{
    Parcel data, reply;
    data.writeInterfaceToken(IMyService::getInterfaceDescriptor());
    data.writeString8(str);
    status_t status = remote()->transact(SET_STR, data, &reply);
}

int32_t BpMyService::doAdd(int32_t a, int32_t b)
{
    Parcel data, reply;
    data.writeInterfaceToken(IMyService::getInterfaceDescriptor());

    data.writeInt32(a);
    data.writeInt32(b);
    status_t status = remote()->transact(DO_ADD, data, &reply);
    int32_t c;
    if (status == NO_ERROR) {
        c = reply.readInt32();
    }
    return c;
}

IMPLEMENT_META_INTERFACE(MyService, "IMyService");

//-----------------------------------------------------------------------------

status_t BnMyService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case GET_STR:
        {
            CHECK_INTERFACE(IMyService, data, reply);
            String8 str = getStr();
            reply->writeString8(str);
            return NO_ERROR;
        } break;

        case SET_STR:
        {
            CHECK_INTERFACE(IMyService, data, reply);
            String8 str = data.readString8();
            setStr(str);
            return NO_ERROR;
        } break;

        case DO_ADD:
        {
            CHECK_INTERFACE(IMyService, data, reply);
            int32_t a = data.readInt32();
            int32_t b = data.readInt32();
            int32_t c = doAdd(a, b);
            reply->writeInt32(c);
            return NO_ERROR;
        } break;

        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

};   //namespace android

MyService.cpp很簡單,最關鍵的是instantiate函式,把MyService加入到ServiceManager中,控制代碼是一個String16型別的字串“my.service”,除此之外,具體業務是在這裡面實現的

#define LOG_TAG "MyService"
#include <stdio.h>
#include <string.h>
#include <utils/Log.h>
#include <utils/Errors.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include "MyService.h"

namespace android {
String8 MyService::mStr;

void MyService::instantiate() {
    defaultServiceManager()->addService(String16("my.service"), new MyService());
}

MyService::MyService() {
    ALOGD("MyService bind Construct");
}

MyService::~MyService() {
    ALOGD("MyService bind deConstruct");
}

String8 MyService::getStr() {
    return mStr;
}

void MyService::setStr(String8 str) {
    mStr = str;
}

int32_t MyService::doAdd(int32_t a, int32_t b) {
    return a+b;
}

};

main_myserver.cpp,server端main函式,通過MyService::instantiate()把MyService加入到安卓服務總管ServiceManager的管控中

#define LOG_TAG "main_myserver"
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include "MyService.h"
using namespace android;
int main(int argc, char** argv)
{
    signal(SIGPIPE, SIG_IGN);
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    MyService::instantiate();
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

MyClient.cpp,實現了Client獲取服務端代理的過程,通過向安卓服務總管ServiceManager查詢“my.service”控制代碼然後再加以轉換得到一個遠端物件gMyService,然後通過gMyService與服務端通訊,實現Client的業務函式

#define LOG_TAG "MyClient"
#include <utils/Log.h>
#include <binder/IServiceManager.h>
#include "MyClient.h"
namespace android {

Mutex MyClient::gMutex;
sp<IMyService> MyClient::gMyService;
sp<MyClient::DeathNotifier> MyClient::gDeathNotifier;

const sp<IMyService>& MyClient::getMyService()
{
    Mutex::Autolock _l(gMutex);
    if (NULL == gMyService.get())
    {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16("my.service"));
            if (binder != 0)
                break;
            ALOGW("my.service not published, waiting...");
            usleep(500000); // 0.5 s
        } while (true);
        if (NULL == gDeathNotifier.get())
        {
            gDeathNotifier = new DeathNotifier();
        }

        binder->linkToDeath(gDeathNotifier);
        gMyService = interface_cast<IMyService>(binder);
    }

    return gMyService;
}

MyClient::DeathNotifier::~DeathNotifier() {
    Mutex::Autolock lock(gMutex);
    if (NULL != gMyService.get()) {
#if PLATFORM_SDK_VERSION < 23
        gMyService->asBinder()->unlinkToDeath(this);
#else
        gMyService->asBinder(gMyService)->unlinkToDeath(this);
#endif
    }
}

void MyClient::DeathNotifier::binderDied(const wp<IBinder>& who) {
    Mutex::Autolock lock(gMutex);
    MyClient::gMyService.clear();
    ALOGW("MyClient binder server died!");
}

//--------------------------------------------------------------------------------------
String8 MyClient::getStr()
{
    return getMyService()->getStr();
}

void MyClient::setStr(String8 str)
{
    return getMyService()->setStr(str);
}

int32_t MyClient::doAdd(int32_t a, int32_t b)
{
    return getMyService()->doAdd(a, b);
}

};  //namespace android

Android使用Binder通訊的實現到此已經差不多完成了,接下來是測試過程:

—-C應用測試
test.cpp , C++應用程式,簡單的呼叫,可以通過logcat檢視結果,測試的時候記得先把Server端跑起來

#define LOG_TAG "test"
#include <stdio.h>
#include <utils/Log.h>
#include "MyClient.h"

using namespace android;

int main(){
    MyClient m;
    m.setStr(String8("hello world"));
    String8 result;
    result = m.getStr();
    ALOGW("getStr , result = %s", result.string()); 
    int32_t sum = m.doAdd(10, 20);  
    ALOGW("doAdd, sum = %d", sum);  
    return 0;
}

結果:
這裡寫圖片描述

—- Android應用測試
要想在Android應用層呼叫C層的客戶端,必須通過JNI介面函式才能實現。
Java中宣告JNI方法,然後通過javah生成jni標頭檔案,呼叫上面Client端的業務函式實現jni標頭檔案宣告的方法,就打通了Java與C++的橋樑。

com_test_androidclient_MainActivity.h檔案,通過javah靜態生成的

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_androidclient_MainActivity */

#ifndef _Included_com_test_androidclient_MainActivity
#define _Included_com_test_androidclient_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_test_androidclient_MainActivity
 * Method:    setStr
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_test_androidclient_MainActivity_setStr
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_test_androidclient_MainActivity
 * Method:    getStr
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_test_androidclient_MainActivity_getStr
  (JNIEnv *, jobject);

/*
 * Class:     com_test_androidclient_MainActivity
 * Method:    doAdd
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_test_androidclient_MainActivity_doAdd
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

com_test_androidclient_MainActivity.cpp, jni橋樑的實現

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include "MyClient.h"
/* Header for class com_test_androidclient_MainActivity */
#ifdef __cplusplus     
extern "C"    
{     
#endif  
using namespace android;
/*
 * Class:     com_test_androidclient_MainActivity
 * Method:    setStr
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_test_androidclient_MainActivity_setStr
  (JNIEnv *env, jobject thiz, jstring jstr){
    MyClient *m = new MyClient();
    sp<MyClient> client(m);
    const char *str = env->GetStringUTFChars(jstr, NULL);
    client->setStr(String8(str));
}

/*
 * Class:     com_test_androidclient_MainActivity
 * Method:    getStr
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_test_androidclient_MainActivity_getStr
  (JNIEnv *env, jobject thiz){
    MyClient *m = new MyClient();
    sp<MyClient> client(m);
    String8 str = client->getStr();
    return env->NewStringUTF(str.string());
}

/*
 * Class:     com_test_androidclient_MainActivity
 * Method:    doAdd
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_test_androidclient_MainActivity_doAdd
  (JNIEnv *env, jobject thiz, jint a, jint b){
    MyClient *m = new MyClient();
    sp<MyClient> client(m);
    return client->doAdd(a, b);
}

#ifdef __cplusplus     
}    
#endif  

結果:
這裡寫圖片描述

可以看到,c或者Android的客戶端都能正確與server端進行互動。

以上就是在Android中使用Binder的簡單步驟,本例中Server端在native世界中,而Client端在native世界與Java世界都有實現,其實也可以把Server端放在Java世界中,native世界的Client也可以通過Binder與其進行通訊。如果Server端和Client端都在Java世界中,那就可以用著名的AIDL來實現了。

本文程式碼地址:
http://download.csdn.net/download/email_jade/9962184
原始碼直接mm編譯即可生成所有的檔案