轉:Android 的進程與線程總結

來源:互聯網
上載者:User

標籤:

當一個Android應用程式組件啟動時候,如果此時這個程式的其他組件沒有正在運行,那麼系統會為這個程式 以單一線程的形式啟動一個新的Linux 進程。 預設情況下,同一應用程式下的所有組件都運行再相同的進程和線程(一般稱為程式的“主”線程)中。如果一個應用組件啟動但這個應用的進程已經存在了(因為 這個應用的其他組件已經在之前啟動了),那麼這個組件將會在這個進程中啟動,同時在這個應用的主線程裡面執行。然而,你也可以讓你的應用裡面的組件運行在 不同的進程裡面,也可以為任何進程添加額外的線程。

AD:51CTO 網+ 第十二期沙龍:大話資料之美_如何用資料驅動使用者體驗

本文翻譯自Android官方文檔

當一個Android應用程式組件啟動時候,如果此時這個程式的其他組件沒有正在運行,那麼系統會為這個程式以單一線程的形式啟動一個新的 Linux 進程。 預設情況下,同一應用程式下的所有組件都運行再相同的進程和線程(一般稱為程式的“主”線程)中。如果一個應用組件啟動但這個應用的進程已經存在了(因為 這個應用的其他組件已經在之前啟動了),那麼這個組件將會在這個進程中啟動,同時在這個應用的主線程裡面執行。然而,你也可以讓你的應用裡面的組件運行在 不同的進程裡面,也可以為任何進程添加額外的線程。

這片文章討論了Android程式裡面的進程和線程如何運作的。

進程

預設情況下,同一程式的所有組件都運行在相同的進程裡面,大多數的應用都是這樣的。然而,如果你發現你需要讓你的程式裡面的某個組件運行在特定的進程裡面,你可以在manifest 檔案裡面設定。

manifest 檔案裡面為每一個組件元素—<activity>, <service>, <receiver>, 和<provider>— 提供了 android:process 屬 性。通過設定這個屬性你可以讓組件運行在特定的進程中。你可以設定成每個組件運行在自己的進程中,也可以讓一些組件共用一個進程而其他的不這樣。你還可以 設定成不同應用的組件運行在同一個進程裡面—這樣可以讓這些應用共用相同的Linux user ID同時被相同的認證所認證。

<application> 元素也支援 android:process 屬性,設定這個屬性可以讓這個應用裡面的所有組件都預設繼承這個屬性。

Android 可能在系統剩餘記憶體較少,而其他直接服務使用者的進程又要申請記憶體的時候shut down 一個進程, 這時這個進程裡面的組件也會依次被kill掉。當這些組件有新的任務到達時,他們對應的進程又會被啟動。

在決定哪些進程需要被kill的時候,Android系統會權衡這些進程跟使用者相關的重要性。比如,相對於那些承載這可見的activities的 進程,系統會更容易的kill掉那些承載不再可見activities的進程。決定是否終結一個進程取決於這個進程裡面的組件啟動並執行狀態。下面我們會討論 kill進程時所用到的一些規則。

進程的生命週期

作為一個多任務的系統,Android 當然系統能夠儘可能長的保留一個應用進程。但是由於新的或者更重要的進程需要更多的記憶體,系統不得不逐漸終結老的進程來擷取記憶體。為了聲明哪些進程需要保 留,哪些需要kill,系統根據這些進程裡面的組件以及這些組件的狀態為每個進程產生了一個“重要性層級” 。處於最低重要性層級的進程將會第一時間被清楚,接著時重要性高一點,然後依此類推,根據系統需要來終結進程。

在這個重要性層級裡面有5個等級。下面的列表按照重要性排序展示了不同類型的進程(第一種進程是最重要的,因此將會在最後被kill):

  1. Foreground 進程 一個正在和使用者進行互動的進程。 如果一個進程處於下面的狀態之一,那麼我們可以把這個進程稱為 foreground 進程:
    • 進程包含了一個與使用者互動的 Activity  (這個 Activity的 onResume() 方法被調用)。
    • 進程包含了一個綁定了與使用者互動的activity的 Service 。
    • 進程包含了一個運行在”in the foreground”狀態的 Service —這個 service 調用了 startForeground()方法。
    • 進程包含了一個正在啟動並執行它的生命週期回呼函數 (onCreate(), onStart(), oronDestroy())的 Service 。
    • 進程包含了一個正在運行 onReceive() 方法的 BroadcastReceiver 。

    一般說來,任何時候,系統中只存在少數的 foreground 進程。 只有在系統記憶體特別緊張以至雩都無法繼續運行下去的時候,系統才會通過kill這些進程來緩解記憶體壓力。在這樣的時候系統必須kill一些 (Generally, at that point, the device has reached a memory paging state,這句如何翻譯較好呢)foreground 進程來保證 使用者的互動有響應。

  2. Visible 進程 一個進程沒有任何 foreground 組件, 但是它還能影響螢幕上的顯示。 如果一個進程處於下面的狀態之一,那麼我們可以把這個進程稱為 visible 進程:
    • 進程包含了一個沒有在foreground 狀態的 Activity ,但是它仍然被使用者可見 (它的 onPause() 方法已經被調用)。這種情況是有可能出現的,比如,一個 foreground activity 啟動了一個 dialog,這樣就會讓之前的 activity 在dialog的後面部分可見。
    • 進程包含了一個綁定在一個visible(或者foreground)activity的 Service 。

    一個 visible 進程在系統中是相當重要的,只有在為了讓所有的foreground 進程正常運行時才會考慮去kill visible 進程。

  3. Service 進程 一個包含著已經以 startService() 方法啟動的 Service 的 進程,同時還沒有進入上面兩種更進階別的種類。儘管 service 進程沒有與任何使用者所看到的直接關聯,但是它們經常被用來做使用者在意的事情(比如在背景播放音樂或者下載網路資料),所以系統也只會在為了保證所有的 foreground and visible 進程正常運行時kill掉 service 進程。
  4. Background 進程 一個包含了已不可見的activity的 進程 (這個 activity 的 onStop() 已 經被調用)。這樣的進程不會直接影響使用者的體驗,系統也可以為了foreground 、visible 或者 service 進程隨時kill掉它們。一般說來,系統中有許多的 background 進程在運行,所以將它們保持在一個LRU (least recently used)列表中可以確保使用者最近看到的activity 所屬的進程將會在最後被kill。如果一個 activity 正確的實現了它的生命週期回呼函數,儲存了自己的目前狀態,那麼kill這個activity所在的進程是不會對使用者在視覺上的體驗有影響的,因為當使用者 回退到這個 activity時,它的所有的可視狀態將會被恢複。查看 Activities 可以擷取更多如果儲存和恢複狀態的文檔。
  5. Empty 進程 一個不包含任何活動的應用組件的進程。 這種進程存在的唯一理由就是緩衝。為了提高一個組件的啟動的時間需要讓組件在這種進程裡運行。為了平衡進程緩衝和相關核心緩衝的系統資源,系統需要kill這些進程。

Android是根據進程中組件的重要性儘可能高的來評級的。比如,如果一個進程包含來一個 service 和一個可見 activity,那麼這個進程將會被評為 visible 進程,而不是 service 進程。

另外,一個進程的評級可能會因為其他依附在它上面的進程而被提升—一個服務其他進程的進程永遠不會比它正在服務的進程評級低的。比如,如果進程A中 的一個 content provider 正在為進程B中的用戶端服務,或者如果進程A中的一個 service 綁定到進程B中的一個組件,進程A的評級會被系統認為至少比進程B要高。

因為進程裡面運行著一個 service 的評級要比一個包含background activities的進程要高,所以當一個 activity 啟動長時操作時,最好啟動一個 service 來 做這個操作,而不是簡單的建立一個worker線程—特別是當這個長時操作可能會拖垮這個activity。比如,一個需要上傳圖片到一個網站的 activity 應當開啟一個來執行這個上傳操作。這樣的話,即使使用者離開來這個activity也能保證上傳動作在後台繼續。使用 service 可以保證操作至少處於”service process” 這個優先順序,無論這個activity發生了什麼。這也是為什麼 broadcast receivers 應該使用 services 而不是簡單的將耗時的操作放到線程裡面。

線程

當一個應用啟動的時候,系統會為它建立一個線程,稱為“主線程”。這個線程很重要因為它負責處理調度事件到相關的 user interface widgets,包括繪製事件。你的應用也是在這個線程裡面與來自Android UI toolkit (包括來自 android.widget 和 android.view 包的組件)的組件進行互動。因此,這個主線程有時候也被稱為 UI 線程。

系統沒有為每個組件建立一個單獨的線程。同一進程裡面的所有組件都是在UI 線程裡面被執行個體化的,系統對每個組件的調用都是用過這個線程進行調度的。所以,響應系統調用的方法(比如 onKeyDown() 方法是用來捕捉使用者動作或者一個生命週期回呼函數)都運行在進程的UI 線程裡面。

比如,當使用者點擊螢幕上的按鈕,你的應用的UI 線程會將這個點擊事件傳給 widget,接著這個widget設定它的按壓狀態,然後發送一個失效的請求到事件隊列。這個UI 線程對請求進行出隊操作,然後處理(通知這個widget重新繪製自己)。

當你的應用與使用者互動對響應速度的要求比較高時,這個單執行緒模式可能會產生糟糕的效果(除非你很好的實現了你的應用)。特別是,當應用中所有的事情 都發生在UI 線程裡面,那些訪問網路資料和資料庫查詢等長時操作都會阻塞整個UI線程。當整個線程被阻塞時,所有事件都不能被傳遞,包括繪製事件。這在使用者看來,這個 應用假死了。甚至更糟糕的是,如果UI 線程被阻塞幾秒(當前是5秒)以上,系統將會彈出臭名昭著的 “application not responding” (ANR) 對話方塊。這時使用者可能選擇退出你的應用甚至卸載。

另外,Android的UI 線程不是安全執行緒的。所以你不能在一個worker 線程操作你的UI—你必須在UI線程上對你的UI進行操作。這有兩條簡單的關於Android單執行緒模式的規則:

  1. 不要阻塞 UI 線程
  2. 不要在非UI線程裡訪問 Android UI toolkit

Worker 線程

由於上面對單一執行緒模式的描述,保證應用介面的及時響應同時UI線程不被阻塞變得很重要。如果你不能讓應用裡面的操作短時被執行玩,那麼你應該確保把這些操作放到獨立的線程裡(“background” or “worker” 線程)。

比如,下面這段代碼在一個額外的線程裡面下載圖片並在一個 ImageView顯示:

  1. new Thread(new Runnable(){ 
  2.     public void run(){ 
  3.         Bitmap b = loadImageFromNetwork("http://example.com/image.png"); 
  4.         mImageView.setImageBitmap(b); 
  5.     } 
  6. }).start();} 

起先這段代碼看起來不錯,因為它建立一個新的線程來處理網路操作。然而,它違反來單一執行緒模式的第二條規則: 不在非UI線程裡訪問 Android UI toolkit—這個例子在一個worker線程修改了 ImageView 。這會導致不可預期的結果,而且還難以調試。

為了修複這個問題,Android提供了幾個方法從非UI線程訪問Android UI toolkit 。詳見下面的這個列表:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

那麼,你可以使用 View.post(Runnable) 方法來修改之前的代碼:

  1. public void onClick(View v){ 
  2.     new Thread(new Runnable(){ 
  3.         public void run(){ 
  4.             final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); 
  5.             mImageView.post(new Runnable(){ 
  6.                 public void run(){ 
  7.                     mImageView.setImageBitmap(bitmap); 
  8.                 } 
  9.             }); 
  10.         } 
  11.     }).start();} 

現在這個方案的安全執行緒的:這個網路操作在獨立線程中完成後,UI線程便會對ImageView 進行操作。

然而,隨著操作複雜性的增長,代碼會變得越來越複雜,越來越難維護。為了用worker 線程處理更加複雜的互動,你可以考慮在worker線程中使用Handler ,用它來處理UI線程中的訊息。也許最好的方案就是繼承 AsyncTask 類,這個類簡化了需要同UI進行互動的worker線程任務的執行。

使用 AsyncTask

AsyncTask 能讓你在UI上進行非同步作業。它在一個worker線程裡進行一些阻塞操作然後把結果交給UI主線程,在這個過程中不需要你對線程或者handler進行處理。

使用它,你必須繼承 AsyncTask 並實現 doInBackground() 回調方法,這個方法運行在一個後台線程池裡面。如果你需要更新UI,那麼你應該實現onPostExecute(),這個方法從 doInBackground() 取出結果,然後在 UI 線程裡面運行,所以你可以安全的更新你的UI。你可以通過在UI線程調用 execute()方法來運行這個任務。

比如,你可以通過使用 AsyncTask來實現之前的例子:

  1. public void onClick(View v){ 
  2.     new DownloadImageTask().execute("http://example.com/image.png"); 
  3. private class DownloadImageTask extends AsyncTask<String,Void,Bitmap>{ 
  4.     /** The system calls this to perform work in a worker thread and 
  5.       * delivers it the parameters given to AsyncTask.execute() */ 
  6.     protected Bitmap doInBackground(String... urls){ 
  7.         return loadImageFromNetwork(urls[0]); 
  8.     } 
  9.     
  10.     /** The system calls this to perform work in the UI thread and delivers 
  11.       * the result from doInBackground() */ 
  12.     protected void onPostExecute(Bitmap result){ 
  13.         mImageView.setImageBitmap(result); 
  14.     }} 

現在UI是安全的了,代碼也更加簡單了,因為AsyncTask把worker線程裡做的事和UI線程裡要做的事分開了。

你應該閱讀一下 AsyncTask 的參考文檔以便更好的使用它。下面就是一個對 AsyncTask 如何作用的快速的總覽:

  • 你可以具體設定參數的類型,進度值,任務的終值,使用的範型
  •  doInBackground() 方法自動在 worker 線程執行
  • onPreExecute()onPostExecute(), 和 onProgressUpdate() 方法都是在UI線程被調用
  •  doInBackground() 的傳回值會被送往 onPostExecute()方法
  • 你可以隨時在 doInBackground()方法裡面調用 publishProgress() 方法來執行UI 線程裡面的onProgressUpdate() 方法
  • 你可以從任何線程取消這個任務

注意: 你在使用worker線程的時候可能會碰到的另一個問題就是因為runtime configuration change (比如使用者改變了螢幕的方向)導致你的activity不可預期的重啟,這可能會kill掉你的worker線程。為瞭解決這個問題你可以參考 Shelves 這個項目。

安全執行緒的方法

在某些情況下,你實現的方法可能會被多個線程所調用,因此你必須把它寫出安全執行緒的。

轉:Android 的進程與線程總結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.