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) 安全性,才能提供穩定可靠的核心服務。、
转自:http://www.android1.net/Topic.aspx?BoardID=21&TopicID=990
作者: 高焕堂