一個月以前,就把IPC看完了,但是這幾天看到Window這一塊和Activity的啟動過程以后,就發現ipc用的地方太多了,沒辦法,又回來復習了一遍,并做了一點筆記。
《android技術開發探索》
多進程:
在Menifest文件中給組件指定android:process屬性,有兩種情況,1進程名為“com.example.ipcdemo:remote”這是當前應用的私有進程,其他應用的組件不可以和他跑在同一個進程中。2 進程名為“com.example.ipcdemo.remote”則屬于全局進程,其他應用通過ShareUID方式就可以和他跑在同一個進程中了(前提是兩個應用的ShareUID相同并且簽名也得相同),這樣就可以互相訪問對方的私有數據、組件信息。
android為每一個進程分配了一個獨立的虛擬機,不同的虛擬機在內存上有不同的地址空間,這就導致了不同的虛擬機在訪問同一個類的對象會拆生多份副本。一般來說,多進程會造成如下幾方面的問題;
靜態成員和單例模式完全失效
線程同步機制完全失效
SharedPreferences的可靠性下降
Application會多次創建
序列化:
Serializable接口(java提供的)和Parcelable接口(android自帶的)
Serializable需要提供一個serialVersionUID,這是為了反序列化可以被成功
Serializable接口:
靜態變量不會參與序列化過程,因為他屬于類而不是對象
用transient關鍵字標記的成員變量不參與序列化過程。
Parcelable接口:
只要實現這個接口,一個類的對象就可以實現序列化并可以通過Intent和Binder傳遞了
兩者都可以進行Intent間的數據傳輸,Serializeable主要用于存儲設備和網絡傳輸,而Praceable主要用于內存上。
聲明一個aidl接口,他會自動給你生成一個java類,在java類中,有幾個方法比較重要:
asInterface()將服務端Binder對象轉化成客戶端所需要的AIDL接口類型的對象,如果是本進程就返回服務端Stub本身,否則返回Stub.proxy對象
asBinder:此方法用于返回Binder對象。
onTransact:這個方法運行在服務端的Binder線程池中,當客戶端發起請求時,遠程請求通過系統底層封裝后交由此方法來處理。服務端通過code確定客戶端所請求的目標方法是什么?接著從data取出目標參數,然后執行目標方法,執行完畢,如果有返回值就想reply中寫入返回值。
proxy#getBookList: 這個方法運行在客戶端,當客戶端遠程調用此方法時,它首先創建該方法所需要的輸入性Parcel對象_data,輸出型_reply和返回值對象List,然后把參數寫入_data中,接著調用transact方法來發起RPC(遠程過程調用),transact方法都做些了什么呢?它其實是一個本地方法,他的實現在Native層,里面進行一系列的函數調用,最終調用了talkWithDriver函數,通信過程交給了驅動來完成,這個函數通過ioctl系統調用,Client陷入了內核態,同時將當前線程掛起。驅動完成一系列的操作之后喚醒Server進程,然后服務端的onTransact方法會被調用,這個方法將結果返回給驅動,驅動將喚醒掛起的Client進程,當前線程繼續執行,并從_reply 中取出結果。
————————————————————————————
進程隔離:為了保護操作系統進程互不干擾而設計的一組不同硬件和軟件的技術。為了避免進程A寫入進程B的情況發生。
用戶空間/內核控件:
Kernel是一個高安全級別的東西,所以用戶代碼一般是不能訪問Kernel的,一般的解決方案都是系統調用,通過一個統一的入口接口,所有的資源訪問都是在內核控制下執行的。當一個任務執行系統調用而陷入內核代碼時,我們就稱為進程處于內核態。
內核模塊/驅動:
Linux的動態可加載模塊(Loadable Kernel Module)解決這個問題,模塊是具有獨立功能的程序,它可以被單獨編譯,但是不能被獨立運行。它在運行時被鏈接到內核作為的一部分在內核空間運行。這樣,android系統可以添加一個模塊運行在內核空間,用戶進程之間通過這個模塊作為橋梁,就可以完成通信了。
在android系統中,運行在內空間的、負責各個用戶進程間的通信通過Binder的內核模塊叫做Binder驅動。(驅動一般是指設備驅動程序,是一種可以是計算機和設備通信的特殊程序)驅動就是操作硬件的接口,為了支持Binder通信過程,Binder使用了一種“硬件”,因此這個模塊被稱為驅動。
為什么要選擇Binder?因為性能和安全//具體就不講了,這兒有更好的回答。
Binder通信模型:
假設A要給B打電話(相當于ClientA要和ServerB通信),肯定需要一個電話本(相當于ServiceManager)和一個基站(Binder驅動)。
進程A和進程B是如何通信的呢?內核可以訪問A和B的所有數據,所以最簡單的方式就是通過內核中專。先把A數據copy到內核,再從內核copy到B中,用戶空間要操作內和空間,就得需要系統調用,即copy_from_user,copy_to_user。
但是Binder不是這么干的!
首先,Server進程向SM(ServiceManager)注冊,然后client向SM查詢,需要一個Server,但是進程間的通信都要經過內核,即Binder驅動,它不會返回給client一個真實的object,而是一個objectProxy,它和真實的object有同樣的功能,它唯一做的事情就是把參數包裝然后交給驅動。然后驅動再把參數給真的Server,然后把結果返回來交給驅動,驅動給client。由于驅動返回的objectProxy太真實了,所以就有一種假象直接把Server進程里面的對象object傳給了client進程,因此,我們可以說Binder對象是可以進行跨進程傳遞的對象。
由于SM和server不在一個進程里,所以Server進程向SM注冊的過程也是跨進程通信,這兒是暗箱操作;SM中存在的也是Client的代理,然后Client向SM查詢的時候,驅動會返回Client另外一個代理,Server進程的對象只有一個,其他的都是他的代理。
一句話總結:client只不過是Server的代理,代理對象協助驅動完成了進程通信。
我們使用AIDL接口經常會碰到這些類,每個類代表什么?
IBinder:是一個接口,代表了一種跨進程的能力,只要實現了這個接口,就能將對象進行跨進程傳輸了,這是驅動底層支持的,數據流經過驅動的時候,驅動會自動識別IBinder類型的數據,從而完成不同進程Binder本地對象以及Binder代理對象的轉換。
IInterface代表的是Server對象具有什么能力,具體的說,就是aidl里面的接口。
Binder和BinderProxy都繼承IBinder,具有遠程傳輸的能力,BinderProxy是Binder的一個內部類,在跨進程時,驅動會自動完成兩個對象的轉換。
編譯工具會給我們生成一個Stub靜態的內部類,它繼承了Binder,說明是一個Binder本地對象,它實現了IInterface接口,說明他有遠程server承諾給client的能力。
我們在bind一個Service之后,在onServiceConnection的回調里面,就是通過asInterface方法拿到一個遠程service的