編輯:關於Android編程
Binder是一種Android實現的IPC,用於進程間通信。
通常Linux系統提供好幾種進程間通信的方式,比如
1) Message Queue :把進程之間通信用的message保存到內核中或者從內核中讀取的方式。
2) Shared Memory:進程間指定共享的內存,把需要傳輸的內容保存到相應的內存的方式
3) Socket:
Android中選擇Binder為主要的IPC方式,因為Binder比其他的IPC方式有更加簡潔快速,消耗內存更小等優點。
下面來看一下Framework層Java空間都是怎麼用Binder實現進程間通信的。
Binder實際是一種C/S模式的工作方式,客戶端通過Binde向服務端發出請求,服務端再通過Binder返回相應的內容給服務端。Binder的工作流程如下:
1)客戶首先獲得服務端的代理對象。所謂代理對象就是客戶端建立一個服務端的”引用”,該代理對象具有服務端的功能,使其在客戶端訪問服務端的方法就像訪問本地方法一樣。
2)客戶端通過調用服務器代理對象的方法,向服務器端發送請求
3)代理對象通過Binder驅動將用戶請求發送給服務器端
4)服務器端處理客戶端請求,並通過Binder驅動將處理結果返回給客戶端
5)客戶端收到服務器端的返回結果
這裡說的服務端為:BBinder 服務端代理對象為:BpBinder
還需要說一下IBinder接口。這個接口是對跨進程對象的抽象,在C/C++和Java都有定義。IBinder定義了一套使用Binder機制來實現客戶程序與服務器的通信協議。
一個對象只能在當前進程中被訪問,如果希望它能被其他進程訪問,就必須實現IBinder接口。IBinder接口可以指向本地對象,也可以指向遠程對象。關鍵在與IBinder接口中的transact函數。如果IBinder指向服務端代理,那transact負責把請求發送給服務器;如果IBinder指向的是一個服務端,那麼transact只負責提供服務。因此不管是服務端還是服務端代理對象,都必須實現該接口,這樣才能進行Binder通信。
C/C++中BBinder和BpBinder都是繼承了IBinder接口的類。
所以實現Binder就很簡單,服務器端繼承BBinder並實現onTransact函數,客戶端繼承BpBinder實現transact函數即可。
以MediaPlayerService為例,在Stagefright.cpp文件的main函數中有如下代碼
spsm = defaultServiceManager(); sp binder = sm->getService(String16("media.player")); sp service = interface_cast (binder); sp omx = service->getOMX();
這裡getOMX函數是BpMediaPlayerService類中的函數,這個很容易看的出來。
定義如下:
virtual spgetOMX() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); remote()->transact(GET_OMX, data, &reply); //調用tranact函數發送Binder請求 return interface_cast (reply.readStrongBinder()); }
上述的Binder請求會最終發給服務器端,也就是BnMediaPlayerService類的onTransact函數中
status_t BnMediaPlayerService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch (code) { ... case GET_OMX: { CHECK_INTERFACE(IMediaPlayerService, data, reply); spomx = getOMX(); reply->writeStrongBinder(omx->asBinder()); return NO_ERROR; } break; ... } }
最終getOMX函數就是調用繼承了BnMediaPlayerService類的MediaPlayerService類中的getOMX函數
spMediaPlayerService::getOMX() { Mutex::Autolock autoLock(mLock); if (mOMX.get() == NULL) { mOMX = new OMX; } return mOMX; }
下面來看一下上述的過程是怎麼通過Binder驅動來傳遞的,因為上面說的內容都沒有涉及到打開,讀寫Binder驅動(/dev/binder)的內容。
要看service是怎麼打開Binder驅動並實現,必須要從其初始化過程以及service的注冊過程看起。
在系統開機的時候,init.rc裡邊有如下配置,負責啟動mediaserver。
service media /system/bin/mediaserver class main user media group system audio camera inet net_bt net_bt_admin net_bw_acct drmrpc sdcard_r shell mediadrm media_rw qcom_diag radio ioprio rt 4
可以從Android.mk配置中找到,上面的service啟動,入口函數是main_mediaserver.cpp文件中的main函數。
int main(int argc __unused, char** argv) { signal(SIGPIPE, SIG_IGN); char value[PROPERTY_VALUE_MAX]; bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1); pid_t childPid; if (doLog && (childPid = fork()) != 0) { //如果doLog為true,其父進程就會跑到這裡,處理幾個子進程的消息。這個不是重點,跳過!! } else { ... spproc(ProcessState::self()); sp sm = defaultServiceManager(); ... waitBeforeAdding(String16("media.player")); MediaPlayerService::instantiate(); ALOGI("Add MediaPlayerService on mediaserver"); // SEC_PRODUCT_FEATURE_AUDIO_COMMON ... ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); } }
來看一下上面幾行代碼的說明
1. spproc(ProcessState::self()) ProcessState沒有初始化就初始化,已經初始化的話就把ProcessState的對象加一個sp。這裡加sp的作用, 可以看Android中sp/wp的說明,這裡就不再贅述。那ProcessState初始化都在干什麼呢? ProcessState::ProcessState() : mDriverFD(open_driver()) //這裡打開Binder驅動設備 , mVMStart(MAP_FAILED) , mManagesContexts(false) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1) { if (mDriverFD >= 0) { //驅動設備的mmap!!! mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); } LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating."); } 2. sp sm = defaultServiceManager() sp defaultServiceManager() { if (gDefaultServiceManager != NULL) return gDefaultServiceManager; { AutoMutex _l(gDefaultServiceManagerLock); while (gDefaultServiceManager == NULL) { gDefaultServiceManager = interface_cast ( ProcessState::self()->getContextObject(NULL)); if (gDefaultServiceManager == NULL) sleep(1); } } return gDefaultServiceManager; } sp ProcessState::getStrongProxyForHandle(int32_t handle) { ... b = new BpBinder(handle); //BpBinder為一個服務器代理客戶端,handle(也就是mHandle)標識一個service。 //一個客戶端在通過transact發送Binder消息給ServiceManager的時候會發送自己的handle。 //ServiceManager會根據這個handle來分發Binder消息給相應的服務端。 //handle為0是ServiceManager本身 //當然getService()找到相應的service不是通過handle,而是通過service名字 ... result = b; } BpBinder強制轉換成IServiceManager?? 3. MediaPlayerService::instantiate() void MediaPlayerService::instantiate() { defaultServiceManager()->addService( // String16("media.player"), new MediaPlayerService()); } BpServiceManager::addService(){ Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name); data.writeStrongBinder(service); data.writeInt32(allowIsolated ? 1 : 0); status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); //按理說應該是BnServiceManager::onTransact()函數接收!!!但其實是 //service_manager.c文件的函數接收處理 return err == NO_ERROR ? reply.readExceptionCode() : err; } 這裡的remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply)按理說應該是傳到 BnServiceManager::onTransact()函數來處理。但ServiceManager比較特殊。其實這個是傳到 service_manager.c文件中去實現了 添加service的操作!!BnServiceManager為什麼沒有用呢??
4.ProcessState::self()->startThreadPool(), IPCThreadState::self()->joinThreadPool()
把相關的Server加到ServiceManger之後,Service會使用ProcessState::self()->startThreadPool()
啟動一個線程(使用IPCThreadState::self()->joinThreadPool()),負責打開Binder驅動等待客戶端發來消
息。下面的IPCThreadState::self()->joinThreadPool()本身也會打開一個Binder設備等待客戶端消息。
看一下客戶端發送Binder消息給對應的Service的流程以及Service接收到Binder消息之後的處理
1.在需要使用到Service完成功能的時候,需要按如下流程發送Binder消息
1) 需要從ServiceManager根據Service的名字,獲取相應的Service(當然如果Service沒有注冊的話是獲取不到 的)。 spsm = defaultServiceManager(); sp binder = sm->getService(String16("media.player")); mOMX = service->getOMX(); 2)打包數據只會通過remote()->transact發送數據 virtual sp getOMX() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); remote()->transact(GET_OMX, data, &reply); return interface_cast (reply.readStrongBinder()); }
2.具體看一下用來發送的am->transact()函數都在干什麼
//am->transact()最終會調用到其父類的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; } IPCThreadState::transact() { IPCThreadState::writeTransactionData(); //負責數據的打包 /* //這裡的cookie和handle負責找到指定的Service並調用相應的onTransact函數 //怎麼通過cookie或者handle找到相應的onTranact函數呢???? tr.target.ptr = 0; tr.target.handle = handle; tr.code = code; tr.flags = binderFlags; tr.cookie = 0; tr.sender_pid = 0; tr.sender_euid = 0; */ IPCThreadState::waitForResponse();//talkWithDriver()發送Binder消息 }
3.以main_mediaserver.cpp文件為例,main函數最後會通過ProcessState::self()->startThreadPool(), IPCThreadState::self()->joinThreadPool()函數開啟線程和打開Binder設備等待客戶端發來消息
//來看一下調用流程 IPCThreadState::joinThreadPool()->IPCThreadState::getAndExecuteCommand() { talkWithDriver(); executeCommand(cmd); } executeCommand()//這個函數如果發現是有Binder Transact會跑到下面部分 { if (tr.target.ptr) { spb((BBinder*)tr.cookie); error = b->transact(tr.code, buffer, &reply, tr.flags); } } //這裡tr.target.pr其實就是tr.target.handle。這裡b->transact()會調用到 //BBinder::transact()->onTransact()然後怎麼調用到子類的onTransact()函數的????? /*if(tr.target.ptr)為真的分支部分是最重要的。它從binder內核驅動中獲取到一個地址並轉換為BBinder類型的指針(該指針在執行IServiceManager::addService()函數時放入binder內核驅動)。記住,MediaPlayerService繼承自BnMediaPlayerService,BnMediaPlayerService又繼承自BBinder。該指針實際上與BnMediaPlayerService實例是同一個指針。於是接下來的transact()函數將調用到BnMediaPlayerService::onTransact()/
Service的注冊以及獲取Servcie,然後客戶端發送請求給服務端的流程如下:
1) Service啟動的時候通過IServiceManager的addService()把自己加到ServiceManager中
2) addService的service參數會在Binder Driver中變成handle變量
3) Binder Driver會根據名字來管理所有的service
4) IServiceManager可以通過getService()找到已經注冊的Service的Interface handle
5) android.os.IBinder.INTERFACE_TRANSACTION code來找到Interface handle的實際名字
到此上面解釋完了Framework層是怎麼通過Binder實現IPC了!!
http://egloos.zum.com/windom/v/1865390
Java層的binder機制主要涉及到以下類: 服務層:IFooService;FooService; FooManager 其中,IFooService 接口定義服務所提供的方法,由.aidl文件實現; FooService具體實現各個功能函數,提供具體服務; FooManager用於注冊和查找該服務; 中間層:IFooService.Stub;IFooService.Stub.Proxy 中間代碼,用於實現框架封裝,實際工作中由.aidl接口文件自動生成; IPC層:Binder;BinderProxy;Parcel BinderProxy為Binder對象在本地的代理,接收客戶端傳過來的數據,對其進行封裝,調用JNI函數將數據發送至底層框架; Binder接收從底層框架傳過來的數據,解包,並傳送至遠程服務。 Parcel類對數據進行包封裝。
int register_android_os_Binder(JNIEnv* env) { if (int_register_android_os_Binder(env) < 0) return -1; if (int_register_android_os_BinderInternal(env) < 0) return -1; if (int_register_android_os_BinderProxy(env) < 0) return -1; if (int_register_android_os_Parcel(env) < 0) return -1; return 0; }
待續。。
前言很多人要實現輪播圖都會想到使用ViewPager + Handler來完成輪播圖的效果。但是在RxJava快速發展的情況下,已經可以使用RxJava來代替Handle
前言:前面介紹了瀑布流的基本實現,實際上瀑布流還有一些事件需要監聽。比如點擊事件,下拉和上拉事件。這裡接著上次的 android—UI—Recyc
效果圖點擊標簽,指示線滑動到當前的標簽下。 用到的技術: activity中添加 fragement, ViewPager, 自定義繪制view-Activity中動態添
先看看效果圖:首先是布局文件<FrameLayout android:layout_width=match_parent android:layout_margin