“Binder通信是同步而不是異步的”,但是在實際使用時,是設計成客戶端同步而服務端異步。
看看Framwork層的各service類java源碼便會知道,在客戶端調用服務端的各種方法時,通常會傳遞一個Binder過來,該Binder對象用於服務端做異步回調,而服務端本身會使用handler或隊列的方式做成異步處理。在Android中,系統service是作為"管理者"的身份存在的,像Ams(ActivityManagerService),它並不創建Activity,創建Activity的是客戶端的ActivityThread,但是每當ActivityThread想創建一個Acitivty,就必須告訴Ams,Ams會進行調度該Acitivty的創建。更簡單的一個例子,Toast和NotificationManagerService,Toast負責彈出窗口的創建顯示和隱藏,而何時顯示跟何時隱藏卻是由NotificationManagerService決定的。具體可以看看這篇文章Toast窗口的源碼分析。
我們可以發現,當隊列本來不為空時,客戶端的同步在服務端執行完將ToastRecord放入隊列之后就可以結束了。服務端會通過連續取出隊列項並且回調客戶端的show()和hide()方法。而在服務度回調show()和hide()時,客戶端就成了”服務端“,這時它通過handler將本次真正的顯示和隱藏Toast窗口操作壓入Message隊列,再一次實現了異步操作。
那為什么要設計成這樣呢?以Toast為例,假如服務端不設計成異步調用+使用Binder回調,那當有2個Toast請求同時過來時,為了實現串行化顯示(Toast每次只顯示一個,本次顯示完畢后才接下去顯示下一個),那么第二個Toast進程就必須阻塞着等待第一個Toast顯示再隱藏,更糟糕的是,即使是第一個請求也必須在顯示之后阻塞着等待服務端發送隱藏指令。
(我在看源碼時對於每次IPC調用客戶端都會傳遞一個Binder對象到服務端覺得很奇怪,當時以為是為了"雙向通信":服務端也可能需要客戶端的一些處理結果。但看了更多代碼后我覺得可能更准確的原因是為了異步回調。以上只是個人理解,如有錯誤請指出。)
