1. 要點:
l
核心服務通常在獨立的進程
(Process)
裡執行。
l
必須提供
IBinder
介面,讓應用程式可以進行跨進程的綁定
(Binding)
和呼叫。
l
因為共用,所以必須確保多線裎安全
(Thread-safe)
。
l
以
C++
類別定義,誕生其物件,透過
SM
之協助,將該物件參考值傳給
IServiceManager::addService()
函數,就加入到
Binder Driver
裡了。
l
應用程式可透過
SM
之協助而遠距綁定該核心服務,此時
SM
會回傳
IBinder
介面給應用程式。
l
應用程式可透過
IBinder::transact()
函數來與核心服務互傳資料。
2.
入門級範例:將
AddService
核心服務加入
Binder Driver
此範例功能為簡單的整數加法
(Add)
運算,此核心服務命名為
AddService
。
Step-1
:
以
C++
撰寫
AddService
類別,其完整程式碼為:
/*--- AddService.h
定義檔
---*/
//AddService.h
#ifndef ANDROID_GUILH_ADD_SERVICE_H
#define ANDROID_GUILH_ADD_SERVICE_H
#include <utils.h>
#include <utils/RefBase.h>
#include <utils/IInterface.h>
#include <utils/Parcel.h>
namespace android {
class AddService : public BBinder{
mutable Mutex mLock;
int32_t mNextConnId;
public:
static int instantiate();
AddService();
virtual ~AddService();
virtual status_t onTransact(
uint32_t, const Parcel&, Parcel*, uint32_t);
}}; //namespace
#endif
/*--- AddService.cpp
實作檔
---*/
// AddService.cpp
#include "AddService.h"
#include <utils/IServiceManager.h>
#include <utils/IPCThreadState.h>
namespace android {
static struct sigaction oldact;
static pthread_key_t sigbuskey;
int AddService::instantiate() {
LOGE("AddService instantiate");
int r = defaultServiceManager()->addService(
String16("guilh.add"), new AddService());
LOGE("AddService r = %d/n", r);
return r;
}
AddService::AddService()
{ LOGV("AddService created");
mNextConnId = 1;
pthread_key_create(&sigbuskey, NULL);
}
AddService::~AddService()
{ pthread_key_delete(sigbuskey);
LOGV("AddService destroyed");
}
status_t AddService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){
switch(code) {
case 0: {
pid_t pid = data.readInt32();
int num = data.readInt32();
num = num + 1000;
reply->writeInt32(num);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}}; //namespace
/*--- Make
檔
---*/
/
/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= /
AddService.cpp
LOCAL_C_INCLUDES := /
$(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := /
libutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := libAdd
include $(BUILD_SHARED_LIBRARY)
執行上述
Android.mk
檔,就產出
libAdd.so
檔案了。
Step-2
:
以
C++
撰寫一個可獨立執行的
addserver.cpp
程式,它的用途是:誕生一個
AddService
類別之物件,然後將該物件參考存入
Binder Driver
裡。其內容為:
/*--- addserver.cpp ---*/
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <utils/IPCThreadState.h>
#include <utils/ProcessState.h>
#include <utils/IServiceManager.h>
#include <utils/Log.h>
#include <private/android_filesystem_config.h>
#include "../libadd/AddService.h"
//#include <libadd/AddService.h>
using namespace android;
int main(int argc, char** argv)
{
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
LOGI("ServiceManager: %p", sm.get());
AddService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
編譯並連結此
addserver.cpp
,產出
addserver
可執行程式。
Step-3
:
上述兩個步驟分別產出了
libAdd.so
類別庫和
addserver
可執行程式了。接著將
libAdd.so
拷貝到
Android
模擬器的
/system/lib/
裡;也把
addserver
拷貝到
/system/bin/
裡。
Step-4
:
執行
addserver
,其中的指令:
AddServer::instantiate();
就執行到
AddServer
類別的
instantiate()
函數,其內容為:
int AddService::instantiate() {
LOGE("AddService instantiate");
int r = defaultServiceManager()->addService(
String16("guilh.add"), new AddService());
LOGE("AddService r = %d/n", r);
return r;
}
其先執行到
new AddServer()
,就誕生一個
AddServer
類別之物件;接著,呼叫
defaultServiceManager()
函數取得
SM
的
IServiceManager
介面,再呼叫
IServiceManager::addServer()
將該物件參考存入
Binder Driver
裡,如下圖所示:
3.
入門級範例:寫個
Add
類別來使用
AddService
核心服務
剛才已經成功地將
AddService
服務加入到
Binder Driver
裡了。現在,茲寫個
C++
應用類別來繫結
(Bind)
此核心服務。
Step-5
:
以
C++
撰寫
Add
類別,其完整程式碼為:
/*--- Add.h ---*/
#ifndef ANDROID_GUILH_ADD_H
#define ANDROID_GUILH_ADD_H
namespace android {
class Add {
public:
void setN(int n);
private:
static const void getAddService();
};
}; //namespace
#endif // ANDROID_GUILH_ADD_H
/*--- Add.cpp ---*/
#include <utils/IServiceManager.h>
#include <utils/IPCThreadState.h>
#include "Add.h"
namespace android {
sp<IBinder> binder;
void Add::setN(int n){
getAddService();
Parcel data, reply;
data.writeInt32(getpid());
data.writeInt32(n);
LOGE("BpAddService::create remote()->transact()/n");
binder->transact(0, data, &reply);
return;
}
const void Add::getAddService(){
sp<IServiceManager> sm = defaultServiceManager();
binder = sm->getService(String16("guilh.add"));
LOGE("Add::getAddService %p/n",sm.get());
if (binder == 0) {
LOGW("AddService not published, waiting...");
return;
}
}
}; //namespace
其中的
setN()
函數呼叫
getAddService()
函數來取得
SM
的介面參考,然後要求
SM
去協助繫結到
AddService
核心服務,完成時
SM
會回傳
BpBinder
物件的
IBinder
介面參考,然後暫存於
binder
變數裡。如下圖所示:
4. 後語
:
Add.cpp通常只是用來測試核心服務而已,你也可以撰寫JNI Native 函數來呼叫核心服務的功能,例如下圖:
此外,撰寫核心服務時,必須確保多線程
(Multi-Thread)
安全性,才能提供穩定可靠的核心服務。