Android進程與線程

         咱們都知道,在操做系統中進程是OS分配資源的最小單位,而線程是執行任務的最小單位。一個進程能夠擁有多個線程執行任務,這些線程能夠共享該進程分配到的資源。當咱們的app啓動運行後,在該app沒有其餘組件正在運行的前提下,Android系統會啓動一個新Linux進程來運行app,這個進程只包含了一個線程在運行。在默認狀況下,app的組件都運行在該進程中,最初就包含的這個線程也被稱爲主線程或者UI線程。若是咱們啓動該app的時候,系統中已經有一個進程在運行該app的組件,那麼該app也會在該進程中運行。固然,咱們也可讓app中不一樣的組件運行在不一樣的進程中,也能夠在任意進程中新開線程執行任務。html

進程

        前面提到過,在默認狀況下同一app的全部組件都是運行在同一進程中的,並且大多數app並不須要去更改這個設定。但若是咱們真的須要指定進程來運行特定組件,那麼能夠在manifest文件中設置。咱們在manifest文件中定義了各個組件,例如activity,service,receiver,provider等等,咱們能夠設置process屬性來指定一個新線程來運行該組件。經過設置process屬性,咱們能夠設置每一個組件都運行在不一樣的進程中,也能夠指定幾個組件運行在同一個進程中。咱們甚至能夠設置不一樣來自app的組件運行在同一個進程中(經過指定相同的process屬性並共享相同的user ID)。數據庫

        以前的文章中提到,Android系統啓動後會載入通用的framework的代碼與資源以後,啓動一個Zygote進程。爲了啓動一個新的程序進程,系統會fork Zygote進程生成一個新的進程,而後在新的進程中加載並運行應用程序的代碼。這就使得大多數的RAM pages被用來分配給framework的代碼,同時促使RAM資源可以在應用的全部進程之間進行共享。緩存

        Android系統在內存不足的狀況下會殺死一些進程來知足那些直接和用戶交互的進程,在這些被殺死的進程中運行的組件也會被註銷掉。當這些組件從新運行時, 纔會啓動該線程。那麼,系統將會如何決定要殺死哪一個進程呢?Android系統須要根據進程與用戶的相關重要性來判斷的。例如,與那些正在顯示activity的進程相比,系統更傾向於殺死那些再也不顯示的activity所在的進程。安全

    進程生命週期

        Android系統會盡量長時間的維持一個進程的運行,可是最終會回收舊進程的內存空間提供給新的進程或是更重要的進程使用。Android系統採用了基於組件運行的進程以及組件狀態的「重要性層級」的策略,根據重要性逐層清除進程。服務器

        重要性層級從高到低共分爲5層:網絡

    一、前臺進程foreground process    多線程

        當前正在與用戶交互的進程。若是一個進程P知足以下任意一個條件,則進程P被稱爲前臺進程:app

  • 當前正在與用戶交互(調用過resume方法)的activity在進程P中運行
  • 某個service與當前正在與用戶交互的activity相互綁定,該service運行於進程P中
  • 某個service調用了startForeground()方法,該service運行在進程P中
  • 某個service正在執行某個生命週期的回調方法,該service運行在進程P中
  • 某個broadcastReceiver正在執行它的onReceive函數,該broadcastReceiver運行在進程P中

        一般狀況下,某時刻系統只會有不多一部分前臺進程存在。它們只會在內存很是低的狀況下才會被殺死,在這種狀況下,設備達到了「memory paging state」狀態,只有殺死一些前臺進程才能保證系統的快速響應。異步

    二、可見進程Visable process    async

        沒有任何前臺組件在此進程中運行,可是仍然能夠影響到用戶所看到的屏幕。若是進程P知足如下任意一個條件,則進程P被稱爲可見進程:

  • 某個activity並不運行於前臺,但仍能被用戶所見(調用了pause方法),activity運行於進程P中。例如某activity啓動了一個dialog,仍然能夠看到該activity。
  • 若是某個service與visible activity或foreground activity綁定,該service運行於進程P中。

        可見進程相對而言比較重要,但在某些狀況下爲了保證前臺進程的運行,系統仍是會殺死這些可見進程的。

    三、服務進程service process    

        若是一個service經過startservice方法啓動,而且不屬於以上兩種更高級別的狀況,那麼運行該service的進程被稱爲service process。儘管該service並不與任何能被用戶看到的組件綁定,可是它們作的工做是用戶關心的,例如音樂播放,文件下載等等。

    四、後臺進程background process

        某個當前不可見的activity(回調了onStop方法)運行於該線程。此類線程沒法直接影響到用戶體驗,系統可能隨時殺死此類進程回收內存供以上三種進程使用。一般狀況下,系統中有多個後臺進程在運行,因此它們被存於一個LRU列表中,從而最不常被用戶用到的進程會先被殺死。若是一個activity正常回調了它的生命週期的函數並存儲了相應的狀態數據,那麼殺死該後臺進程是不會影響到用戶體驗的。由於當用戶試圖返回到該activity時,系統會恢復該activity全部的狀態。

    五、空進程    empty process

        該進程中沒有運行任何組件,保持此類進程存在的惟一緣由就是等待任務。一旦有組件須要運行,則能夠縮短進程啓動時間。因此係統每每會殺死這些進程用來平衡進程緩存和底層內核緩存之間的系統資源。

        Android系統中,一個進程的等級是能夠動態提高的,由於其餘的進程可能會依賴於該進程。某個進程爲其餘進程提供服務,那麼該進程的等級必定不會低於它所服務的進程。例如,某content provider 運行於進程A中,它爲處於進程B中的客戶端B提供服務;或者若是處於進程A的service綁定於位於進程B中的組件,那麼A進程的重要等級只會高於或等於進程B。

        因爲一個運行service的進程等級要高於那些運行處於後臺activity的進程,若是咱們須要有長時間執行的操做,那麼從一個activity中啓動一個service來完成這些操做就比在activity中新開子線程來完成這些操做效果要好(特別是這些子線程的持續時間要比activity長的狀況下)。例如,一個activity想要上傳一張圖片給服務器,那麼應當開啓一個service在後臺來完成上傳操做,即便用戶離開了當前activity,這些操做也可以在後臺完成。使用service能夠保證這些操做至少具備service優先級,不管當前activity的狀態是否改變。這也是爲何broadcast receiver應當使用service而不是簡單的把耗時操做放在子線程中的緣由。

 

線程

        當應用程序啓動後,系統將會建立一個主線程來運行應用程序。主線程很是重要,它負責爲適當的用戶控件分發任務和事件,包括繪製任務等等。同時,主線程也負責UI組件和應用程序的交互,因此咱們也稱主線程爲UI線程。

        系統並不會爲每一個組件單獨開啓一個線程來運行,全部的組件都會在主線程中初始化並運行運行在同一個進程中,系統經過主線程來調用每一個組件。因此,系統回調方法(例如onKeyDown,生命週期回調方法等)一般運行於主線程。

       例如,當用戶點擊屏幕上的按鈕,UI線程會將點擊事件分發給控件。控件就會設置自身的按下狀態,並將重繪請求添加到事件請求隊列。UI線程從事件隊列中取出該重繪請求後,通知該控件重繪。

       當用戶和app交互頻繁時,單線程的模式可能會致使響應速度慢,用戶體驗不盡人意。若是在主線程中進行網絡或數據庫請求等耗時操做,則會致使線程阻塞,主線程將沒法調度分發事件和任務。當超過5s的阻塞會使系統彈出ANR窗口。另外,UI控件都不是線程安全的,因此係統規定只能在UI線程中修改控件。咱們須要遵循兩個規則:

  1. 不要使UI線程阻塞
  2. 不要在UI線程以外修改控件

worker線程

       上面討論了只有UI線程工做的狀況。爲了提升應用程序UI的響應速度,得到更好的用戶體驗,咱們須要把耗時操做放在子線程中來完成。可是咱們須要注意的是,不要在子線程中操做UI控件。咱們一般使用Android的Handler機制來解決線程間通訊的問題,詳細請參看以前的文章Android線程間異步通訊機制源碼分析。同時,Android也提供了async task來完成異步任務。

異步任務ASYNC TASK

       async task在子線程中執行耗時任務,而後將結果返回給UI線程,無需本身手動建立handler。關於async task的使用就不在這裏介紹了,在使用async task的過程當中,咱們須要注意的是多線程問題。因爲運行配置的問題(例如屏幕橫豎方向改變),會致使子線程任務未通過咱們容許就從新啓動執行。

線程安全方法

       多數狀況下,咱們的方法有可能被多個線程所調用,因此咱們必須考慮到線程安全的問題。特別是對於那些能夠被遠程調用的方法更是如此,例如,綁定service的方法。當咱們試圖調用在IBinder中實現的方法,若是調用者和IBinder處於同一個進程,那麼方法將會在調用者所在線程中執行。若是調用者與IBinder並不處於同一個進程中,那麼系統從所維護的線程池中取出一個線程來執行該方法,該線程池與IBinder運行在同一個進程中(並不是在UI線程中執行)。舉個栗子,儘管service的進程的UI線程將會調用service的onBind方法,然而在onBind方法所返回的IBinder對象中實現的那些方法就會被線程池中線程執行。由於一個service能夠由多個客戶端訪問,線程池中的多個線程能夠在同一時刻調用同一方法。因此IBinder對象中實現的方法須要是線程安全的。

       相似的,一個ContentProvider能夠接收到來自不一樣進程的數據請求,雖然CP和CR類中隱藏了進程間通訊管理的細節,可是CP中對應的查詢,刪除,修改,插入等請求方法將會被交給CP所在進程的線程池中線程來執行。這些方法可能在同一時刻被多個進程所調用,因此這些方法必須是線程安全的。

進程間通訊

       Android系統提供了遠程調用RPC機制來完成進程通訊IPC,經過RPC機制,應用程序中的組件(例如activity)做爲調用者在本地調用某個方法,該方法在遠程(另一個進程中)執行,而後將結果返回給調用者。這就須要將所調用方法和它的數據解析爲操做系統能夠理解的程度,而後從本地進程和地址空間傳遞給遠程的進程和地址空間後,再進行重組和執行。

相關文章
相關標籤/搜索