Android的進程與線程

Android的進程與線程:html

一.Android中的進程

默認狀況下, 同一個application中的全部component運行在同一個linux進程下. java

啓動一個component A時, 若是已存在處於運行狀態中的component B, 且A和B屬於同一個application, 那麼component A將在component B所在的進程下運行,不然將爲A建立一個新的linux進程. linux

開發者也能夠爲application中的component指定不一樣的運行進程. android

manifest.xml文件中的<activity>, <service>, <receiver>, <provider>標籤都支持android:process屬性, 經過這個屬性, 能夠爲component指定運行的進程. 程序員

<application>標籤也支持設定android:process屬性, 用於爲application下的全部component指定默認的運行進程.緩存


進程優先級

內存不足時:

當系統的內存不足時, android系統將根據進程優先級選擇殺死一些不過重要的進程. 安全

進程優先級從高到低分別爲:

1. 前臺進程. 如下的進程爲前臺進程:多線程

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

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

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中的多線程機制, 請參博文http://coolxing.iteye.com/blog/1208371 


永遠要記得:

1. 不要阻塞UI線程. 若是在UI線程中執行阻塞或者耗時操做會致使UI線程沒法響應用戶請求.

2. 不能在非UI線程(也稱爲工做線程)中更新UI, 這是由於android的UI控件都是線程不安全的.


由上所述, 開發者常常會啓動工做線程完成耗時操做或阻塞操做, 若是須要在工做線程的執行期間更新UI狀態, 則應該通知UI線程來進行. 


三.線程間通訊

請看下面的代碼:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.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線程中執行.

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

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() {
                // 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線程中執行.

採用這種方式也能夠修正例子中的錯誤之處:

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.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);
    }
}


五.Handler的sendEmptyMessage(int what)和sendMessage(Message msg)的區別

其實二者沒區別,請看下面Handler的源代碼:

public final boolean sendEmptyMessage(int what){
   return sendEmptyMessageDelayed(what, 0);
}

就是調用了sendEmptyMessageDelayed()而已,下面看下這個方法:

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}

而sendMessage(Message msg)的實現和上面同樣,請看:

public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}

原來在sendEmptyMessageDelayed中就是構建了一個Message,而後把這個Message的what設置成sendEmptyMessage方法中的What參數便可。

在主線程中,Looper類的loop()經過調用: msg.target.dispatchMessage(msg),調用Hanler類的dispatchMessage(Message msg)方法,從而在主線程中處理了這個Message.

若是對Handler機制要深刻了解的,請參考博客文章:http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html。

相關文章
相關標籤/搜索