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編譯即可生成所有的檔案