android進程優先級

[這篇文章是我對dev Guide中Processes and Threads的翻譯和總結, 也加上瞭一些個人理解]

 

android中的進程

默認情況下, 同一個application中的所有component運行在同一個linux進程下. 啟動一個component A時, 如果已存在處於運行狀態中的component B, 且A和B屬於同一個application, 那麼component A將在component B所在的進程下運行. 否則將為A創建一個新的linux進程.

開發者也可以為application中的component指定不同的運行進程. manifest.xml文件中的<activity>, <service>, <receiver>, <provider>標簽都支持android:process屬性, 通過這個屬性, 可以為component指定運行的進程. <application>標簽也支持設定android:process屬性, 用於為application下的所有component指定默認的運行進程.

 

進程優先級

當系統的內存不足時, android系統將根據進程優先級選擇殺死一些不太重要的進程. 進程優先級從高到低分別為:

1. 前臺進程. 以下的進程為前臺進程:

a. 進程中包含處於前臺的正與用戶交互的activity;

b. 進程中包含與前臺activity綁定的service;

c. 進程中包含調用瞭startForeground()方法的service;

d. 進程中包含正在執行onCreate(), onStart(), 或onDestroy()方法的service;

e. 進程中包含正在執行onReceive()方法的BroadcastReceiver.

系統中前臺進程的數量很少, 前臺進程幾乎不會被殺死. 隻有當內存低到無法保證所有的前臺進程同時運行時才會選擇殺死某個前臺進程.

2. 可視進程. 以下進程為可視進程:

a. 進程中包含未處於前臺但仍然可見的activity(調用瞭activity的onPause()方法, 但沒有調用onStop()方法). 典型的情況是運行activity時彈出對話框, 此時的activity雖然不是前臺activity, 但其仍然可見.

b. 進程中包含與可見activity綁定的service.

可視進程不會被系統殺死, 除非為瞭保證前臺進程的運行而不得已為之.

3. 服務進程. 進程中包含已啟動的service.

4. 後臺進程. 進程中包含不可見的activity(onStop()方法調用後的activity). 後臺進程不會直接影響用戶體驗, 為瞭保證前臺進程/可視進程/服務進程的運行, 系統隨時都有可能殺死一個後臺進程. 一個正確的實現瞭生命周期方法的activity處於後臺時被系統殺死, 可以在用戶重新啟動它時恢復之前的運行狀態.

5. 空進程. 不包含任何處於活動狀態的進程是一個空進程. 系統經常殺死空進程, 這不會造成任何影響. 空進程存在的唯一理由是為瞭緩存一些啟動數據, 以便下次可以更快的啟動.

 

進程優先級的額外說明

1. 系統會賦予進程盡可能高的優先級. 例如一個進程既包含已啟動的service, 也包含前臺activity, 則這個進程會被視為前臺進程.

2. 由於組件之間的依賴性, 進程的優先級有可能被提高. 假如進程A服務於進程B, 則進程A的優先級不能低於進程B. 比如, 進程A的ContentProvider組件正在服務於進程B的某個組件, 或者進程A的service組件和進程B的某個組件綁定等, 這些情況下, 進程A的優先級都不會低於進程B(如果按照優先級規則, 進程A的優先級確實低於進程B, 則系統會選擇提高進程A的優先級到和進程B相同).

3. 由於服務進程的優先級高於後臺進程, 因此如果activity需要執行耗時操作, 最好還是啟動一個service來完成. 當然, 在activity中啟動子線程完成耗時操作也可以, 但是這樣做的缺點在於, 一旦activity不再可見, activity所在的進程成為後臺進程, 而內存不足時後臺進程隨時都有可能被系統殺死(但是啟動service完成耗時操作會帶來數據交互的問題, 比如耗時操作需要實時更新UI控件的狀態的話, service就不是一個好的選擇). 基於同樣的考慮, 在BroadcastReceiver中也不應該執行耗時操作, 而應該啟動service來完成(當然, BroadcastReceiver的生命周期過於短暫, 也決定瞭不能在其中執行耗時操作).

 

android中的線程

系統不會為進程中的每一個組件啟動一個新的線程, 進程中的所有組件都在UI線程中實例化. 關於android中的多線程機制, 請參考我的另一篇博文/kf/201205/132323.html

永遠要記得:

1. 不要阻塞UI線程. 如果在UI線程中執行阻塞或者耗時操作會導致UI線程無法響應用戶請求.

2. 不能在非UI線程(也稱為工作線程)中更新UI, 這是因為android的UI控件都是線程不安全的.

由上所述, 開發者經常會啟動工作線程完成耗時操作或阻塞操作, 如果需要在工作線程的執行期間更新UI狀態, 則應該通知UI線程來進行.

 

線程間通信

請看下面的代碼:

Java代碼 
public void onClick(View v) { 
    new Thread(new Runnable() { 
        public void run() { 
            Bitmap b = loadImageFromNetwork("/wp-content/images1/20181012/20120519094618735370.png"); 
            mImageView.setImageBitmap(b); 
        } 
    }).start(); 

上面的代碼是錯誤的, mImageView.setImageBitmap(b)違反瞭第二條準則–不能在工作線程中更新UI.

線程間通信可以解決工作線程如何通知UI線程更新控件的問題. android提供瞭3種線程間通信的方案:

1. 調用以下方法:

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable, long)

如果在工作線程中調用瞭這3個方法, 那麼方法中Runnable參數封裝的操作會在UI線程中執行.

使用這種方式可以修正例子中的錯誤之處:

Java代碼 
public void onClick(View v) { 
    new Thread(new Runnable() { 
        public void run() { 
            final Bitmap bitmap = loadImageFromNetwork("/wp-content/images1/20181012/20120519094618735370.png"); 
            mImageView.post(new Runnable() { 
                // run方法會在UI線程中執行 
                public void run() { 
                    mImageView.setImageBitmap(bitmap); 
                } 
            }); 
        } 
    }).start(); 

2. Handler機制. Handler機制允許開發者在工作線程中調用與UI線程綁定的handler對象的sendMessage()方法向UI線程的消息隊列發送一條消息, UI線程會在適當的時候從消息隊列中取出消息並完成處理.

3. 使用AsyncTask類. 創建一個AsyncTask類的子類, 並根據需要選擇覆寫onPreExecute(), doInBackground(), onProgressUpdate(), onPostExecute()方法.AsyncTask類的具體使用方法請參看文檔, 以下是一些大概的說明:

a. AsyncTask類是一個泛型類, 存在3個泛型參數. 第一個參數指定execute方法的參數類型, 第二個參數指定onProgressUpdate()方法的參數類型, 第三個參數指定 doInBackground()方法的返回值類型以及onPostExecute()方法的參數類型.

b. 執行流程: 在UI線程中調用AsyncTask類的execute方法(隻有該步驟是由程序員控制的)–>系統調用onPreExecute(), 這個方法在UI線程中執行–>系統調用doInBackground()方法, 這個方法在工作線程中執行–>在doInBackground()方法中每調用一次publishProgress()方法, 就會在UI線程中執行一次onProgressUpdate()方法–>doInBackground()方法執行完成後, 系統將調用 onPostExecute()方法, 並將doInBackground()方法的返回值傳遞給 onPostExecute()方法的形參.  onPostExecute()方法在UI線程中執行.

采用這種方式也可以修正例子中的錯誤之處:

Java代碼 
public void onClick(View v) { 
    new DownloadImageTask().execute("/wp-content/images1/20181012/20120519094618735370.png"); 

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { 
    protected Bitmap doInBackground(String… urls) { 
        return loadImageFromNetwork(urls[0]); 
    } 
     
    protected void onPostExecute(Bitmap result) { 
        mImageView.setImageBitmap(result); 
    } 

 

摘自 fhy_2008的專欄

發佈留言