Android 並發編程:(一)基礎知識 —— 架構和組件,android架構
本章節所有內容皆為原創,如需轉載,請註明出處。
http://blog.csdn.net/manoel/article/details/38462631
寫在前面的話
很久沒寫部落格了,一是因為自身水平有限,怕誤人子弟;二是因為感覺沒什麼可寫的;三是因為時間有限,要尋找工作、學習和生活之間的平衡。
最近一直在研究和梳理Android多線程編程的東西,希望能夠把這些分享給大家。
想必做過Android應用開發的同學應該都會知道,多線程是一個特別“詭異”的地方。之所以稱為“詭異”,是因為多線程產生的bug是非常難以複現的,而且是毫無規律的。
又有什麼比“毫無規律”讓程式員感到可怕的呢。
如果對Android開發還不是特別瞭解的同學,可以參考我之前翻譯的Android開發教程。
http://blog.csdn.net/column/details/development4android.html
近期,我會不定時地更新一些關於Android多線程的原理和技巧,都是我自己對一些實踐的總結,希望能做到深入淺出。
好了,進入正題。想要瞭解Android多線程的機制,還是先從整個Android的架構說起吧。
軟體棧
-
Applications
-
應用程式層,包括使用Java庫和Android架構所編寫的程式。
-
Core Java
-
Java類庫,並不是完全的Java SE或Java ME的實現,而是Apache Harmony的一個實現,基於Java5。這一層提供基礎的Java線程機制,包括java.lang.Thread類和java.util.concurrent包。
-
Application framework
-
Android架構層,主要負責處理視窗系統,UI組件,資源等。基本上,要編寫一個Android應用所需要的所有類都在這一層。同時,Android架構定義和管理Android組件的生命週期和它們之間的通訊。而且,Android架構定義了一系列Android特有的非同步機制,包括HandlerThread,AsyncTask,IntentService,AsyncQueryHandler和Loaders。
-
Native libraries
-
C/C++庫層,主要負責處理映像,音視頻,資料庫,字型,OpenGL等。Java所編寫的程式並不直接和這一層交流,因為Android架構已經對這些本地代碼進行了封裝。
-
Runtime
-
運行時環境,即Dalvik虛擬機器。為每一個Android應用程式提供了一個沙箱,Dalvik虛擬機器執行.dex檔案。每一個程式都運行在它專有的Dalvik虛擬機器中。
-
Linux kernel
-
Linux核心,即潛在的作業系統。負責讓程式使用硬體功能,例如聲音,網路,相機等。同時,負責管理進程和線程。為每一個程式開啟一個進程,每一個進程掌管一個Dalvik虛擬機器。在進程中,多線程執行程式的代碼。Linux核心通過調度機制,為進程和它們的線程分配可用的CPU執行時間。
程式架構
一個程式的基礎是Application對象以及Android的組件:Activity,Service,BroadcastReceiver和ContentProvicer。
程式
一個程式在Java中的表達方式就是android.app.Application對象。當程式開啟的時候,這個對象被建立,程式停止時被銷毀。也就是說,Application對象經曆一個Linux進程的完整生命週期。當Linux進程重新開啟的時候,一個新的Application對象也被建立了。
組件
Android的組件包括Activity,BroadcastReceiver,Service和ContentProvider。
這些實體有不同的責任和生命週期,但它們都代表了程式的入口,也就是程式啟動的地方。
一個組件喚醒另一個組件是通過Intent才能實現的,這個過程可以發生在同一個程式中或多個程式之間。Intent分為顯式的和隱式的:
-
顯式的 Intent
-
定義一個組件的完整的名字,這個名字在運行時能夠能夠被程式識別。
-
隱式的 Intent
-
通過IntentFilter在運行時與組件綁定,需要在IntentFilter中定義一些“協議”。如果Intent與IntentFilter中的“協議”匹配,那麼這個組件就會被啟動。
組件的生命週期是Android系統特定的技術,它們不與潛在的Java對象匹配。一個Java對象可以比它的組件存活的時間要久,並且,Dalvik虛擬機器可以儲存與一個組件的對應的多個對象。小心,這會導致記憶體流失。我會在後面對多線程造成的記憶體流失進行分析。
通常,實現一個組件的方式是子類化它。並且,一個程式中所有的組件都要在AndroidManifest.xml中進行註冊。
Activity
一個Activity就是一個螢幕,通常佔據了裝置的全部螢幕尺寸。它用來展示資訊,處理使用者的輸入等。包括所有的UI組件,例如按鈕,文字框,圖片等。
Activity持有一個對所有視圖樹的對象引用,所以它佔據的記憶體會變的很大。
當使用者在不同的螢幕之間導航的時候,Activity執行個體從一個棧裡面被初始化。當導航到一個新的螢幕時,一個新的Activity被壓入到棧裡,當返回到之前的螢幕時,Activity被從棧中彈出。
在下面的圖中,首先開啟了一個Activity A,接著切換到B,同時A銷毀;然後切換到C和D。A,B,C是全螢幕顯示的,但是D是視窗模式的,僅僅佔據了螢幕的一部分空間。A完全被銷毀了,B是不可見的,C是部分可見的,D是全部可見的,並且D在棧頂。因此,D獲得了焦點並且可以接收使用者的輸入。Activity在棧裡面的位置,決定了Activity的狀態:
- 可見並且處於活躍狀態: D
- 暫停並且部分可見: C
- 停止並且完全不可見: B
- 不可互動並且已經完全銷毀: A
一個程式中最頂端的Activity,對這個程式在系統中的優先順序是有一定影響的。這裡所謂的優先順序就是進程的優先順序,這將影響一個程式被終止的幾率和分配給這個程式中線程的執行時間。
按返回鍵或者調用finish()方法都會終止一個Activity的生命週期。
Service
Service是在後台執行的,它是不可見的,和使用者之間也沒有任何互動的。可以用來從其他組件卸載一些操作,這些操作比那些組件活的要長。一個Service可以用過兩種方式去執行,start或bind:
-
Started Service
-
通過調用Context.startService(Intent)開啟服務,這裡的Intent既可以是隱式的,也可以是顯式的。如果想要停止服務,調用 Context.stopService(Intent)。
-
Bound Service
-
通過調用Context.bindService(Intent, ServiceConnection, int),多個組件可以綁定同一個Service,這裡的Intent既可以是隱式的,也可以是顯式的。綁定之後,通過ServiceConnection介面與Service進行通訊。如果想要停止服務,調用 Context.unbindService(ServiceConnection)。當最後一個組件與Service解除綁定後,這個Service就會被銷毀。
ContentProvider
一個程式想要共用大量的資料,不管是在這個程式中,還是多個程式之間,都可以使用ContentProvider。
它提供了擷取資料的入口,通常適合SQLite資料庫一起使用。SQLite資料庫是對程式私人的,但是通過ContentProvider就可以實現進程之間的資料共用了。
BroadcastReceiver
它監聽從一個程式之中,程式之間,或系統發出的Intent。總而言之,就是它監聽手機中所有的Intent,然後過濾這些收到的Intent,確定哪一個需要被處理。
一個BroadcastReceiver應該動態註冊,也就是說註冊在需要使用它的地方,這樣可以對它進行控制。
如果靜態註冊在了AndroidManifest.xml中,那麼當這個程式在安裝的時候,它就一直在監聽Intent,不管這些Intent對它是不是有用。
如此一來,如果某一個Intent匹配了某一個IntentFilter,那麼這個BroadcastReceiver就會開啟與之關聯的程式。
這裡詳細說一下,假設有一個程式A,在AndroidManisest.xml中註冊了一個監聽網路變化的BroadcastReceiver。
此時,A沒有被開啟,也就是說此時系統中並沒有A的進程。
當系統的網路發生變化時,因為這個BroadcastReceiver的存在,A的進程被建立了,A對應的Application對象也會被建立!
因為BroadcastReceiver被認定是進入一個程式的入口,這種入口還包括Activity,Service和ContentProvider。
並發編程
一般說並發,最常見的情況就是許多人在一小段時間內都點擊了你的網站,發出了處理請求
並發是指發,不是處理
並發編程是對並髮狀況的應對,不是說你只有一個處理器你就應對不了了,這個處理方案和架構以及演算法有關。CPU一般是分時的,會在極短的時間內不停地切換給不同的線程使用,無論多少並發都會處理下去,只是時間問題。
如何提高處理效率就看採用的技術了。
錯誤提示