1.線程進(jìn)程
默認(rèn)情況下,一個(gè)應(yīng)用分配一個(gè)進(jìn)程,同一應(yīng)用的所有組件都在相同的進(jìn)程和線程中運(yùn)行。
四大組件可以通過(guò)設(shè)置 android:proccess 指定組件運(yùn)行的線程,application 也支持 android:proccess 屬性,以設(shè)置所有組件的默認(rèn)值。
通過(guò)設(shè)置 android:process 可以使不同應(yīng)用的組件在同一進(jìn)程中運(yùn)行,這些應(yīng)用享有相同的Linux UserID( android:sharedUserId屬性)和證書(shū)簽名。
Android為每個(gè)安裝好的應(yīng)用程序分配了自己的UID,故進(jìn)程的UID是鑒別進(jìn)程身份的重要標(biāo)志。
系統(tǒng)內(nèi)存不足時(shí),系統(tǒng)會(huì)選擇相對(duì)不重要的進(jìn)程殺死,其優(yōu)先級(jí)如下:
前臺(tái)進(jìn)程
正在交互的Activity(已調(diào)用Activity的onResume)
正在交互的Activity綁定的Service
前臺(tái)運(yùn)行的Sercive(Service調(diào)用startForeground)
正在執(zhí)行生命周期方法的Service(onCreate,onStart或onDestory)
正在執(zhí)行onReceive()的BroadcastReceiver
可見(jiàn)進(jìn)程 沒(méi)有任何前臺(tái)組件,但仍然能被用戶(hù)看到內(nèi)容的進(jìn)程。
調(diào)用 onPause 方法的Activity(例如前臺(tái)組件部分透明,允許顯示下一層的Activity)。
調(diào)用 onPause 方法的Activity綁定的Service。
服務(wù)進(jìn)程
正在運(yùn)行已使用 startService() 方法啟動(dòng)的服務(wù)且不屬于上述兩個(gè)更高類(lèi)別進(jìn)程的進(jìn)程(例如,在后臺(tái)播放音樂(lè)或從網(wǎng)絡(luò)下載數(shù)據(jù))。
后臺(tái)進(jìn)程 已經(jīng)執(zhí)行onStop的Activity。這些進(jìn)程會(huì)被放在LRU列表中,以確保用戶(hù)最近查看最后一個(gè)被終止。
空進(jìn)程 不含任何應(yīng)用組件的進(jìn)程。保留這種進(jìn)程的的唯一目的是用作緩存,以縮短下次在其中運(yùn)行組件所需的啟動(dòng)時(shí)間。
因此耗時(shí)操作的Activity最好為該耗時(shí)操作啟動(dòng)服務(wù)而不是創(chuàng)建線程。例如:正在上傳圖片的Activity啟動(dòng)服務(wù)來(lái)上傳,這樣即使用戶(hù)推出Activity,操作仍然可以在后臺(tái)進(jìn)行。
線程安全
當(dāng)某些方法可能會(huì)從多個(gè)線程調(diào)用時(shí),編寫(xiě)時(shí)必須考慮線程安全。
Binder方法的調(diào)用方如果在同一進(jìn)程,則該方法在調(diào)用方的線程執(zhí)行。
Binder方法的調(diào)用方如果不在同一進(jìn)程,方法將在線程池(Binder所在的進(jìn)程維護(hù))中被調(diào)用,即使調(diào)用方是在UI線程中執(zhí)行,Binder方法依然會(huì)被在線程池中執(zhí)行后返回。
可能會(huì)有多個(gè)池線程在同一時(shí)間使用Binder方法。因此,Binder 方法必須實(shí)現(xiàn)為線程安全方法。
同理ContentProvider的增刪改查方法也是在進(jìn)程的線程池中調(diào)用,而不是在UI線程。由于這些方法可能會(huì)同時(shí)從任意數(shù)量的線程調(diào)用,因此它們也必須實(shí)現(xiàn)為線程安全方法。
2.IPC
多進(jìn)程模式
進(jìn)程間通信(IPC,Inter-Process Communication),指至少兩個(gè)進(jìn)程或線程間傳送數(shù)據(jù)或信號(hào)的一些技術(shù)或方法。
通過(guò)給四大組件指定 android:proccess 屬性,只有這一種方法可以正常開(kāi)啟多進(jìn)程模式。
多進(jìn)程模式中,不同進(jìn)程的組件會(huì)擁有獨(dú)立的虛擬機(jī),Application,內(nèi)存。
通過(guò)JNI在native層中fork一個(gè)新的進(jìn)程,是不常用的創(chuàng)建多線程的方法。
入口Activity如果不指定process屬性,那么它運(yùn)行在默認(rèn)進(jìn)程中,默認(rèn)進(jìn)程的進(jìn)程名是包名,例如android:process=":"。
:的含義是指要在當(dāng)前的進(jìn)程名前附加上當(dāng)前的包名。com.xxx.text:remote == :remote。
進(jìn)程名以:開(kāi)頭的屬于私有進(jìn)程。而進(jìn)程名不以:開(kāi)頭的屬于全局進(jìn)程,其他應(yīng)用可以通過(guò)ShareUID和她跑在統(tǒng)一進(jìn)程中。
Android為每個(gè)進(jìn)程都分配了一個(gè)虛擬機(jī),不同的虛擬機(jī)訪問(wèn)同一個(gè)對(duì)象會(huì)產(chǎn)生多份副本,導(dǎo)致數(shù)據(jù)不一致,所以需要IPC。
3.序列化
Serializable 和 Pracelable 接口可以完成對(duì)象的序列化。
Serializable是JAVA提供的序列化接口,只需要在類(lèi)中聲明serialVersionUID,即可自動(dòng)實(shí)現(xiàn)默認(rèn)的序列化過(guò)程。
對(duì)象序列化只要使用ObjectOutputStream和ObjectInputStream即可。
如果不手動(dòng)指定serialVersionUID,反序列化時(shí)類(lèi)有所改變,將導(dǎo)致crash。
靜態(tài)成員變量不屬于對(duì)象,所以不參與序列化。
用transient關(guān)鍵字編輯的成員變量不參與序列化過(guò)程,用來(lái)修飾無(wú)意義的不用序列化的變量。
Serializable 系統(tǒng)默認(rèn)的序列化過(guò)程也是可以改變的,重寫(xiě)writeObject和readObject即可。
系統(tǒng)已經(jīng)為我們提供了許多實(shí)現(xiàn)Parcelable接口的類(lèi),是可以直接序列化的,Intent,Bundle,Bitmap等。List和Map也可以序列化,前提是他們里面的類(lèi)都是可序列化的。
使用Parcelable需要重寫(xiě)指定方法。
Serializable是JAVA中的序列化接口,使用簡(jiǎn)單但開(kāi)銷(xiāo)巨大,序列化個(gè)反序列化需要進(jìn)行大量I/O操作;Parcelable是Android中的序列化方式,使用麻煩但是效率高,Android推薦。
Parcelable主要用在內(nèi)存序列化上(intent傳遞)。將對(duì)象序列化到存儲(chǔ)設(shè)備或序列化后網(wǎng)絡(luò)傳輸通過(guò)Parcelable會(huì)稍顯復(fù)雜,這兩種情況建議使用Serializable。
4.Binder連接池
多個(gè)不同的業(yè)務(wù)模塊需要使用AIDL進(jìn)行進(jìn)程間通訊,為了減少Service的數(shù)量,將所有的AIDL放在同一個(gè)Service中進(jìn)行管理。
各個(gè)模塊像服務(wù)端提供自己的唯一標(biāo)識(shí)和與其對(duì)應(yīng)的Binder對(duì)象;服務(wù)端至于要一個(gè)Service,然后提供一個(gè)queryBinder接口,這個(gè)接口根據(jù)相應(yīng)的模塊來(lái)返回對(duì)應(yīng)的Binder。
5.Socket
Socket也稱(chēng)套接字,是網(wǎng)絡(luò)通信中的概念。
分為流式套接字和用戶(hù)數(shù)據(jù)報(bào)套接字,對(duì)應(yīng)網(wǎng)絡(luò)傳輸控制層中的TCP和UDP協(xié)議。
TCP面向連接的協(xié)議,提供穩(wěn)定的雙向通信功能。經(jīng)過(guò)三次握手才能完成,自身提供超時(shí)重傳機(jī)制,穩(wěn)定性強(qiáng)。
UDP是面向過(guò)程,無(wú)連接的,提供不穩(wěn)定的單向通訊,當(dāng)然也可以實(shí)現(xiàn)雙向。UDP有較好的性能,缺點(diǎn)是穩(wěn)定性不強(qiáng)。
Socket的使用:
在配置文件中聲明網(wǎng)絡(luò)訪問(wèn)的權(quán)限(不能在主線程中訪問(wèn)網(wǎng)絡(luò))。
遠(yuǎn)程Service建立一個(gè)TCP服務(wù)(通過(guò)ServerSocket對(duì)象),然后在主界面中連接TCP服務(wù)。
重連機(jī)制:每次連接失敗后嘗試重新建立連接,為了降低重試機(jī)制的開(kāi)銷(xiāo),加入休眠機(jī)制,即線程休眠1000毫秒。
Activity退出時(shí)(OnDestory),需要關(guān)閉Socket。
Socket可以實(shí)現(xiàn)進(jìn)程間通訊,也可以實(shí)現(xiàn)設(shè)備間通信,前提是設(shè)備之間的IP地址互相可見(jiàn)。
6.ContentProvider
ContentProvider底層實(shí)現(xiàn)也是Binder。
ContentProvider進(jìn)程中,除了onCreat由系統(tǒng)回調(diào)并運(yùn)行在主線程里,其他五個(gè)方法均有外界回調(diào)發(fā)生在Binder線程池中。
雖然ContentProvider的底層數(shù)據(jù)看起來(lái)像SQLLite數(shù)據(jù)庫(kù),其實(shí)它對(duì)底層數(shù)據(jù)的數(shù)據(jù)沒(méi)有任何要求。
ContentProvider還支持文件數(shù)據(jù),比如圖片視頻表格等,處理這類(lèi)數(shù)據(jù)時(shí)可以在ContentProvider返回文件的句柄給外界從而讓文件來(lái)訪問(wèn)ContentProvider中的文件信息。
Android系統(tǒng)所提供的MediaStore功能就是文件類(lèi)型的ContentProvider。
ContentProvider的使用:
聲明時(shí),android:authorities 是唯一標(biāo)識(shí),建議命名時(shí)加上包名前綴。而且可以通過(guò)控制permission來(lái)控制外界訪問(wèn)的權(quán)限。
ContentProvider的三次query方法可能運(yùn)行在三個(gè)不同的線程中,一個(gè)Binder線程池中。
除了query方法,update,delete,insert方法會(huì)引起數(shù)據(jù)源的改變,可以通過(guò)注冊(cè)registerContentResever來(lái)注冊(cè)觀察者,unregister解除觀察者。通過(guò)ContentResever的notifyChange通知外界數(shù)據(jù)已經(jīng)改變。
增刪改查是存在多線程并發(fā)的,因此方法內(nèi)部需要做好線程同步。比如只使用一個(gè)SQLite對(duì)象連接,因?yàn)镾QLiteDatabase內(nèi)部對(duì)數(shù)據(jù)庫(kù)的操作是有同步處理的。
ContentProvider除了支持對(duì)數(shù)據(jù)源的增刪改查之外,還支持自定義調(diào)用,通過(guò)ContentResever和ContentProvider的Call方法來(lái)完成。