自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android 的進程與線程總結(jié)

移動開發(fā) Android
當一個Android應用程序組件啟動時候,如果此時這個程序的其他組件沒有正在運行,那么系統(tǒng)會為這個程序以單一線程的形式啟動一個新的Linux 進程。 默認情況下,同一應用程序下的所有組件都運行再相同的進程和線程(一般稱為程序的“主”線程)中。如果一個應用組件啟動但這個應用的進程已經(jīng)存在了(因為 這個應用的其他組件已經(jīng)在之前啟動了),那么這個組件將會在這個進程中啟動,同時在這個應用的主線程里面執(zhí)行。然而,你也可以讓你的應用里面的組件運行在 不同的進程里面,也可以為任何進程添加額外的線程。

本文翻譯自Android官方文檔

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

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

進程

默認情況下,同一程序的所有組件都運行在相同的進程里面,大多數(shù)的應用都是這樣的。然而,如果你發(fā)現(xiàn)你需要讓你的程序里面的某個組件運行在特定的進程里面,你可以在manifest 文件里面設(shè)置。

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

<application> 元素也支持 android:process 屬性,設(shè)置這個屬性可以讓這個應用里面的所有組件都默認繼承這個屬性。

Android 可能在系統(tǒng)剩余內(nèi)存較少,而其他直接服務用戶的進程又要申請內(nèi)存的時候shut down 一個進程, 這時這個進程里面的組件也會依次被kill掉。當這些組件有新的任務到達時,他們對應的進程又會被啟動。

在決定哪些進程需要被kill的時候,Android系統(tǒng)會權(quán)衡這些進程跟用戶相關(guān)的重要性。比如,相對于那些承載這可見的activities的 進程,系統(tǒng)會更容易的kill掉那些承載不再可見activities的進程。決定是否終結(jié)一個進程取決于這個進程里面的組件運行的狀態(tài)。下面我們會討論 kill進程時所用到的一些規(guī)則。

進程的生命周期

作為一個多任務的系統(tǒng),Android 當然系統(tǒng)能夠盡可能長的保留一個應用進程。但是由于新的或者更重要的進程需要更多的內(nèi)存,系統(tǒng)不得不逐漸終結(jié)老的進程來獲取內(nèi)存。為了聲明哪些進程需要保 留,哪些需要kill,系統(tǒng)根據(jù)這些進程里面的組件以及這些組件的狀態(tài)為每個進程生成了一個“重要性層級” 。處于***重要性層級的進程將會***時間被清楚,接著時重要性高一點,然后依此類推,根據(jù)系統(tǒng)需要來終結(jié)進程。

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

  1. Foreground 進程 一個正在和用戶進行交互的進程。 如果一個進程處于下面的狀態(tài)之一,那么我們可以把這個進程稱為 foreground 進程:

    一般說來,任何時候,系統(tǒng)中只存在少數(shù)的 foreground 進程。 只有在系統(tǒng)內(nèi)存特別緊張以至于都無法繼續(xù)運行下去的時候,系統(tǒng)才會通過kill這些進程來緩解內(nèi)存壓力。在這樣的時候系統(tǒng)必須kill一些 (Generally, at that point, the device has reached a memory paging state,這句如何翻譯較好呢)foreground 進程來保證 用戶的交互有響應。

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

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

  3. Service 進程 一個包含著已經(jīng)以 startService() 方法啟動的 Service 的 進程,同時還沒有進入上面兩種更高級別的種類。盡管 service 進程沒有與任何用戶所看到的直接關(guān)聯(lián),但是它們經(jīng)常被用來做用戶在意的事情(比如在后臺播放音樂或者下載網(wǎng)絡數(shù)據(jù)),所以系統(tǒng)也只會在為了保證所有的 foreground and visible 進程正常運行時kill掉 service 進程。
  4. Background 進程 一個包含了已不可見的activity的 進程 (這個 activity 的 onStop() 已 經(jīng)被調(diào)用)。這樣的進程不會直接影響用戶的體驗,系統(tǒng)也可以為了foreground 、visible 或者 service 進程隨時kill掉它們。一般說來,系統(tǒng)中有許多的 background 進程在運行,所以將它們保持在一個LRU (least recently used)列表中可以確保用戶最近看到的activity 所屬的進程將會在***被kill。如果一個 activity 正確的實現(xiàn)了它的生命周期回調(diào)函數(shù),保存了自己的當前狀態(tài),那么kill這個activity所在的進程是不會對用戶在視覺上的體驗有影響的,因為當用戶 回退到這個 activity時,它的所有的可視狀態(tài)將會被恢復。查看 Activities 可以獲取更多如果保存和恢復狀態(tài)的文檔。
  5. Empty 進程 一個不包含任何活動的應用組件的進程。 這種進程存在的唯一理由就是緩存。為了提高一個組件的啟動的時間需要讓組件在這種進程里運行。為了平衡進程緩存和相關(guān)內(nèi)核緩存的系統(tǒng)資源,系統(tǒng)需要kill這些進程。

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

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

因為進程里面運行著一個 service 的評級要比一個包含background activities的進程要高,所以當一個 activity 啟動長時操作時,***啟動一個 service 來 做這個操作,而不是簡單的創(chuàng)建一個worker線程—特別是當這個長時操作可能會拖垮這個activity。比如,一個需要上傳圖片到一個網(wǎng)站的 activity 應當開啟一個來執(zhí)行這個上傳操作。這樣的話,即使用戶離開來這個activity也能保證上傳動作在后臺繼續(xù)。使用 service 可以保證操作至少處于”service process” 這個優(yōu)先級,無論這個activity發(fā)生了什么。這也是為什么 broadcast receivers 應該使用 services 而不是簡單的將耗時的操作放到線程里面。

線程

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

系統(tǒng)沒有為每個組件創(chuàng)建一個單獨的線程。同一進程里面的所有組件都是在UI 線程里面被實例化的,系統(tǒng)對每個組件的調(diào)用都是用過這個線程進行調(diào)度的。所以,響應系統(tǒng)調(diào)用的方法(比如 onKeyDown() 方法是用來捕捉用戶動作或者一個生命周期回調(diào)函數(shù))都運行在進程的UI 線程里面。

比如,當用戶點擊屏幕上的按鈕,你的應用的UI 線程會將這個點擊事件傳給 widget,接著這個widget設(shè)置它的按壓狀態(tài),然后發(fā)送一個失效的請求到事件隊列。這個UI 線程對請求進行出隊操作,然后處理(通知這個widget重新繪制自己)。

當你的應用與用戶交互對響應速度的要求比較高時,這個單線程模型可能會產(chǎn)生糟糕的效果(除非你很好的實現(xiàn)了你的應用)。特別是,當應用中所有的事情 都發(fā)生在UI 線程里面,那些訪問網(wǎng)絡數(shù)據(jù)和數(shù)據(jù)庫查詢等長時操作都會阻塞整個UI線程。當整個線程被阻塞時,所有事件都不能被傳遞,包括繪制事件。這在用戶看來,這個 應用假死了。甚至更糟糕的是,如果UI 線程被阻塞幾秒(當前是5秒)以上,系統(tǒng)將會彈出臭名昭著的 “application not responding” (ANR) 對話框。這時用戶可能選擇退出你的應用甚至卸載。

另外,Android的UI 線程不是線程安全的。所以你不能在一個worker 線程操作你的UI—你必須在UI線程上對你的UI進行操作。這有兩條簡單的關(guān)于Android單線程模型的規(guī)則:

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

Worker 線程

由于上面對單一線程模型的描述,保證應用界面的及時響應同時UI線程不被阻塞變得很重要。如果你不能讓應用里面的操作短時被執(zhí)行玩,那么你應該確保把這些操作放到獨立的線程里(“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();} 

起先這段代碼看起來不錯,因為它創(chuàng)建一個新的線程來處理網(wǎng)絡操作。然而,它違反來單一線程模型的第二條規(guī)則: 不在非UI線程里訪問 Android UI toolkit—這個例子在一個worker線程修改了 ImageView 。這會導致不可預期的結(jié)果,而且還難以調(diào)試。

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

那么,你可以使用 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();} 

現(xiàn)在這個方案的線程安全的:這個網(wǎng)絡操作在獨立線程中完成后,UI線程便會對ImageView 進行操作。

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

使用 AsyncTask

AsyncTask 能讓你在UI上進行異步操作。它在一個worker線程里進行一些阻塞操作然后把結(jié)果交給UI主線程,在這個過程中不需要你對線程或者handler進行處理。

使用它,你必須繼承 AsyncTask 并實現(xiàn) doInBackground() 回調(diào)方法,這個方法運行在一個后臺線程池里面。如果你需要更新UI,那么你應該實現(xiàn)onPostExecute(),這個方法從 doInBackground() 取出結(jié)果,然后在 UI 線程里面運行,所以你可以安全的更新你的UI。你可以通過在UI線程調(diào)用 execute()方法來運行這個任務。

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

  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.     }} 

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

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

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

線程安全的方法

在某些情況下,你實現(xiàn)的方法可能會被多個線程所調(diào)用,因此你必須把它寫出線程安全的。

[[124306]]

文地址:Android的進程與線程

責任編輯:閆佳明 來源: greenrobot.me
相關(guān)推薦

2018-07-06 14:00:55

Linux進程線程

2018-05-31 10:57:31

Linux系統(tǒng)進程線程

2015-09-08 15:13:35

Android進程與線程

2009-09-16 08:43:51

linux進程線程

2021-05-21 09:36:42

開發(fā)技能代碼

2012-05-04 09:49:34

進程

2022-06-07 07:37:40

線程進程開發(fā)

2022-02-07 11:55:00

linux進程線程

2022-05-26 08:31:41

線程Java線程與進程

2019-06-03 09:13:11

線程進程多線程

2013-04-25 09:55:21

進程線程

2012-06-20 14:07:28

多線程架構(gòu)單線程

2019-02-26 11:15:25

進程多線程多進程

2011-07-25 10:00:59

項目

2019-09-18 15:09:50

進程線程操作系統(tǒng)

2022-10-12 09:01:52

Linux內(nèi)核線程

2021-06-11 11:28:22

多線程fork單線程

2023-03-05 16:12:41

Linux進程線程

2017-09-11 15:35:43

AndroidInput系統(tǒng)框架

2016-10-09 20:15:30

多線程多進程
點贊
收藏

51CTO技術(shù)棧公眾號