並且已經存在這個應用的線程了(因為有這個應用程式的另一個組件已經運行了),於是這個組件就會在這個已有的進程中啟動並且運行在同一個線程中.然而,你完全可以安排不同的組件運行於不同的進程,並且你可以為任何程式建立另外的線程.
進程
預設下,同一個程式的所有組件都運行在同一個進程中並且大多數程式不必改變這一狀況.然而,如果你非要與眾不同,也可以通過修改manifest檔案實現.
manifest檔案中的所有支援android:process屬性的那些項(<activity>,<service>, <receiver>,和<provider>)都可以指定一個進程,於是這些組件就會在這個進程中運行.你可以設定這個屬性使每個組件運行於其自己的進程或只是其中一些組件共用一個進程.你也可以設定android:process以使不同應用的組件們可以運行於同一個進程—假如這些應用共用同一個使用者ID並且有相同的數位憑證.
<application>元素也支援android:process屬性,用於為所有的組件指定一個預設值.
Android可能在某些時刻決定關閉一個進程,比如記憶體很少了並且另一個進程更迫切的需要啟動時.進程被關閉時,其中的組件們都被銷毀.如果重新需要這些組件工作時,進程又會被建立出來.
當決定關閉哪些線程時,Android系統會衡量進程們與使用者的緊密程度.例如,比起一個具有可見的activity的進程,那些所含activity全部不可見的進程更容易被關閉.如何決定一個進程是否被關閉,取決於進程中啟動並執行組件們的狀態.決定關閉進程的規則將在下面討論.
進程的生命期
Android系統會盡量維持一個進程的生命,直到最終需要為新的更重要的進程騰出記憶體空間。為了決定哪個該殺哪個該留,系統會跟據運行於進程內的組件的和組件的狀態把進程置於不同的重要性等級。當需要系統資源時,重要性等級越低的先被淘汰。
重要性等級被分為5個檔。下面列出了不同類型的進程的重要性等級(第一個進程類型是最重要的,也是最後才會被殺的):
1前台進程
使用者當前正在做的事情需要這個進程。如果滿足下面的條件,一個進程就被認為是前台進程:
這個進程擁有一個正在與使用者互動的Activity(這個Activity的onResume() 方法被調用)。
這個進程擁有一個綁定到正在與使用者互動的activity上的Service。
這個進程擁有一個前台啟動並執行Service — service調用了方法 startForeground().
這個進程擁有一個正在執行其任何一個生命週期回調方法(onCreate(),onStart(), 或onDestroy())的Service。
這個進程擁有正在執行其onReceive()方法的BroadcastReceiver。
通常,在任何時間點,只有很少的前台進程存在。它們只有在達到無法調合的矛盾時才會被殺--如果記憶體太小而不能繼續運行時。通常,到了這時,裝置就達到了一個記憶體分頁調度狀態,所以需要殺一些前台進程來保證使用者介面的反應
2可見進程
一個進程不擁有運行於前台的組件,但是依然能影響使用者所見。滿足下列條件時,進程即為可見:
這個進程擁有一個不在前台但仍可見的Activity(它的onPause()方法被調用)。當一個前台activity啟動一個對話方塊時,就出了這種情況。
3服務進程
一個可見進程被認為是極其重要的。並且,除非只有殺掉它才可以保證所有前台進程的運行,否則是不能動它的。
這個進程擁有一個綁定到可見activity的Service。
一個進程不在上述兩種之內,但它運行著一個被startService()所啟動的service。
儘管一個服務進程不直接影響使用者所見,但是它們通常做一些使用者關心的事情(比如播放音樂或下載資料),所以系統不到前台進程和可見進程活不下去時不會殺它。 4後台進程
一個進程擁有一個當前不可見的activity(activity的onStop()方法被調用)。
這樣的進程們不會直接影響到使用者體驗,所以系統可以在任意時刻殺了它們從而為前台、可見、以及服務進程們提供儲存空間。通常有很多後台進程在運行。它們被儲存在一個LRU(最近最少使用)列表中來確保擁有最近剛被看到的activity的進程最後被殺。如果一個activity正確的實現了它的生命週期方法,並儲存了它的目前狀態,那麼殺死它的進程將不會對使用者的可視化體驗造成影響。因為當使用者返回到這個activity時,這個activity會恢複它所有的可見狀態。
5空進程
一個進程不擁有入何active組件。
保留這類進程的唯一理由是快取,這樣可以提高下一次一個組件要運行它時的啟動速度。系統經常為了平衡在進程快取和底層的核心快取之間的整體系統資源而殺死它們。
跟據進程中當前活動的組件的重要性,Android會把進程按排在其可能的最進階別。例如,如果一個進程擁有一個service和一個可見的activity,進程會被定為可見進程,而不是服務進程。
另外,如果被其它進程所依賴,一個進程的層級可能會被提高—一個服務於其它進程的進程,其層級不可能比被服務進程低。
因為擁有一個service的進程比擁有一個後台activitie的進程層級高,所以當一個activity啟動一個需長時間執行的操作時,最好是啟動一個服務,而不是簡單的建立一個背景工作執行緒。尤其是當這個操作可能比activity的生命還要長時。例如,一個向網站上傳圖片的activity,應該啟動一個service,從而使上傳操作可以在使用者離開這個activity時繼續在後台執行。使用一個service保證了這個操作至少是在"服務進程"層級,而不用管activity是否發生了什麼不幸。這同樣是廣播接收者應該使用service而不是簡單地使用一個線程的理由。
| 代碼如下 |
複製代碼 |
url: http://www.111cn.net/help/zt/21705.html |
android進程與線程詳解二:線程
線程
當一個應用被啟動,系統建立一個執行線程,叫做"main"。這個線程是十分重要的,因為它主管向使用者介面控制項派發事件。其中包含繪圖事件。它也是你的應用與介面工具包(android.widget和 android.view包中的組件)互動的地方。於是main線程也被稱為介面線程。
系統不會為每個組件的執行個體分別建立線程。所有運行於一個進程的組件都在介面線程中被執行個體化,並且系統對每個組件的調用都在這個線程中派發。 結果,響應系統調用的方法(比如報告使用者動作的onKeyDown()或一個生命週期回調方法)永遠在介面線程中進程。
例如,當使用者觸控螢幕幕上的一個按鈕時,你的應用的介面線程把觸摸事件派發給控制項,然後控制項設定它的按下狀態再向事件隊列發出一個自己介面變得無效的請求,介面線程從隊列中取出這個請求並通知這個控制項重繪它自己。
當你的應用在響應使用者互動時需執行大量運算時,這種單線程的模式會帶來低效能,除非你能正確的最佳化你的程式。特別的,如果所有事情都在介面線程中發生,執行比如網路連接或資料庫請求這樣的耗時操作,將會阻止整個介面的響應。當線程被阻塞時,就不能派發事件了,包括繪圖事件。從使用者的角度看,程式反應太慢了。甚至更糟的是,如果介面線程被阻塞幾秒鐘(大5秒鐘吧),使用者就戶抱怨說程式沒反應了,使用者可能因而退出並刪掉你的應用。
此外,Andoid介面不是安全執行緒的。所以你絕不能在一個背景工作執行緒中操作你的介面—你只能在介面線程中管理的你的介面。所以,對於單線程模式有兩個簡單的規則:
1 不要阻塞介面線程
2不要在介面線程之外操作介面。
背景工作執行緒
由於上述的單線程模式,不要阻塞你的介面線程以使你的應用的介面保持響應是非常重要的,那麼如果你有不能很快完成的任務,你應把它們放在另一個線程中執行(後台線程或背景工作執行緒)。
例如,下面是的代碼是響應click事件,在另外一個線程中下載一個圖片並在一個ImageView中顯示它:
| 代碼如下 |
複製代碼 |
public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("http://example.com/image.png"); mImageView.setImageBitmap(b); } }).start(); } |
第一眼,這看起來能很好的工作,因為它建立了一個新線程來進行網路操作。然而它違反了第二條規則:不要在介面線程之外操作介面—它簡單的在背景工作執行緒中修改了ImageView。這會導至未定義的異常出現,並且難以調試追蹤。
為了能改正這個問題,Android提供了很多從其它線程來操作介面的方法。下面是可用的方法們:
| 代碼如下 |
複製代碼 |
1 Activity.runOnUiThread(Runnable) 2 View.post(Runnable) 3 View.postDelayed(Runnable,long) |
例如,你可以用View.post(Runnable)來修正上面的問題:
| 代碼如下 |
複製代碼 |
public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start(); } |
現在這個實現終於是安全執行緒的了:網路操作在另一個線程中並且ImageView 在介面線程中改變。