android進程和線程

轉載請註明出處:http://blog.csdn.net/zhouli_csdn/article/details/45367353


進程和線程(Processes and Threads)

如果一個應用啓動它的一個組件(activity,service,receiver,provider)時,沒有其它的組件正在運行,安卓系統會爲該應用創建一個進程。默認情況下,同一應用的所有組件運行在同一個進程和線程(主線程)。如果一個應用組件啓動時已經存在一個進程(假設爲A)(因爲已經存在一個該應用的組件),那麼該組件將會在同一進程A中運行。當然,你也可以讓不同的組件運行在不同的進程中,併爲他們創建其它的線程。

本文討論了安卓進程和線程在應用中是怎麼工作的。

進程

         默認情況下,同一應用程序的所有組件運行在同一進程中,並且不能改變這種情況。但是,如果你想控制一個組件在一個單獨的進程中,你可以在manifest文件中配置。
manifest文件爲每一種組件都支持android:process屬性配置該組件運行的進程。你可以設置這個屬性使每一個組件運行在自己的進程中,或者一些運行在一個進程中,而其他的運行在各自的進程中。你也可以設置andorid:process屬性,使具有相同linux user ID和相同簽名(are signed with the same certificates)的不同應用程序的組件運行在同一進程中。

Application元素可以設置所有組件的默認進程。

在內存不足並且其他要爲用戶服務的進程請求的情況下,Android可能關閉一個進程。運行在該進程中的組件也會被銷燬,當有任務時,會被重新啓動。

在決定結束哪一個進程依據運行在進程中的組件的狀態。

進程生命週期(Process lifecycle

Android會儘可能的保持一個應用程序進程直到需要爲新的進程或者更重要的進程回收內存。Android依據進程中正在運行的組件和這些組件的狀態劃分了一個重要性層次結構。最低等級的將會被最先殺死,然後是更低等級的,直到可以回覆系統資源。

Andorid爲進程劃分了五個重要性層次結構,前面的是最重要的。

1.前臺進程(Foreground process

一個進程正在爲用戶正在做的事情服務。如果一個進程有如下條件會被認爲前臺進程:
    1.擁有一個Activity(與用戶交互的Activity的onResume方法被調用)
    2.擁有一個綁定到正與用戶交互的Activity的Service
    3.擁有一個Service調用了startForeground()方法
    4.擁有一個正在執行它的生命週期函數(onCreate,onStart或onStop)的Service
    5.擁有一個正在執行onReceive方法的BroadcastReceiver
一般來說,任何時候只有少量的前臺進程存在。只有在系統內存嚴重不足(通常這時候設備到達了內存分頁狀態)下,纔會殺死前臺進程。

2.可見進程(Visible process

一個沒有前臺組件,但是仍然可以影響用戶屏幕上的內容。如果一個進程具備以下條件被認爲可見進程:
    1.擁有一個不在前臺的Activity(onPause方法被調用)。例如前臺Activity打開一個對話框,並且允許之前的Activity在後面看見。
    2.擁有一個綁定到可見或者前臺Activity 的Service。
可見進程一般不會被殺死,除非這樣做可以保證所有前臺進程運行。

3.服務進程(Service process

   擁有一個由startService方法啓動的Service,並且不在1和2這兩個等級中。儘管服務用戶看不見,但是他們一般做用戶比較關心的事情,例如後臺播放音樂或者下載網絡數據。

4.後臺進程(Background process

   擁有一個用戶看不見的Activity(onStop方法被調用)。這些進程對用戶體驗沒有直接影響,系統可以隨時殺死他們。通常會有很多後臺進程運行,它們被保存在一個LRU(最近最少使用)表中。如果一個Activity實現其生命週期方法正確,並且保存了它當前的狀態。當系統殺死它們後,用戶返回到該Activity後,該Activity會恢復它的保存狀態,所以對用戶體驗影響不大。

5.空進程

   一個不包含任何應用程序組件的進程。保持的唯一原因就是爲了緩存,當一個組件需要運行它時,提高啓動時間。系統經常會殺死這些進程,以平衡整個系統進程緩存和底層內核緩存之間的資源。

    Android進程排名依據進程擁有的最高級別。例如一個進程如果擁有一個服務和一個可見Activity,那麼該進程會被認爲是一個可見進程。
    
    此外,如果一個進程被其它進程所依賴,那麼他的優先級會增加。例如:如果A進程中content provider正在服務一個B進程或者A進程的service被綁定到B進程的一個組件,那麼A至少會被認爲和B是一樣重要的。
    
   因爲擁有一個服務的進程優先級比擁有後臺Activities的進程優先級高,一個Activity啓動一個長時間運行的操作要在服務中,而不是簡單的創建一個線程(如果這個操作時間比Activity長)。例如一個Activity上傳一個圖片到一個網站上,開啓一個服務上傳圖片可以在Activity到後臺時讓然可以上傳。用service操作保證至少擁有服務進程優先級,不管Activity發生了什麼。同樣的原因,Receiver也要使用Service來執行長時間的操作,而不是簡單的使用線程。

線程(Threads

當應用程序啓動時,系統會爲應用創建一個執行線程(主線程)。這個線程非常重要,它負責控件事件的分發和屏幕繪製,也負責應用程序和控件之間的相互作用。因此,主線程也被叫做UI線程。

系統不會爲每一個組件的實例創建一個線程。所有運行在相同進程的組件都在UI線程實例化,系統對每一個組件的調用都從UI線程發出。因此,系統的回調函數(例如onKeydown和生命週期函數)總是在UI線程執行。

例如:當用戶點擊一個Button時,UI線程將touch事件分發給button,button會設置自己的點擊狀態併發送一個invalidate request到事件隊列(event queue),UI線程拿到請求,並通知button重繪自己。

當你的應用程序執行繁多的操作以相應用戶的交互,這種單線程模型會表現不佳,除非你正確的實現你的應用程序。
具體來說,如果你在UI線程執行長時間的操作,例如網絡訪問,數據庫操作,將會使UI線程阻塞。當UI線程被阻塞,事件將不能被分發,屏幕也不能繪製。從用戶的角度來看應用掛起了。更糟糕的是,如果UI線程被阻塞超過5秒鐘,用戶將會被呈現臭名昭著的應用程序無響應("application not responding" (ANR)對話框。

Andorid UI工具包是非線程安全的,因此不能從一個工作線程去直接操作UI,必須在UI線程操作UI。因此對於Android單線程模型有兩個規則:
    1.不阻塞UI線程
    2.不從工作線程訪問Android UI toolkit

工作線程(Worker threads

基於以上單線程模型的描述,它的響應能力是至關重要的,你不能阻塞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();
}
但是它違反了單線程模型的第二個規則,爲了解決這個問題Android提供了幾種方式從其它線程訪問UI線程:
    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();
}
然而,隨着操作的複雜性,這種代碼會變得很複雜和難以維護,你可以考慮使用Handler。更好的解決方案可以繼承
AsyncTask類,這簡化了需要和用戶界面交互的工作線程。

使用AsyncTask(Using AsyncTask

AsyncTask允許你在用戶接口執行異步工作,他在工作線程執行阻塞操作並將結果發到UI線程,不需要自己實現handler操作。
你必須實現AsyncTask的子類,並在doInBackground()回調方法,它是執行在一個線程池中的線程,你可以在onPostExecute()中操作UI。然後必須在UI線程中調用execute()。例如:
public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }
    
    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}
AsyncTask使代碼比較簡單,容易維護:
     1.doInBackground()在工作線程
   2.onPreExecute(),onPostExecute(),onProgressUpdate()工作在UI線程
   3.doInBackground()返回的值被髮送到onPostExecute()
   4.你可以在doInBackground()中隨時調用publishProgress()以執行onProgressUpdate()方法。
   5.你可以在任何時候從任何線程取消任務。
警告:你可能在由於runtime configuration change意外重啓Activity的時候,破壞你的工作線程。你可以查看如何在重啓時候保持task,以及在Activity被destroyed時候取消task。

線程安全的方法

當在其它進程調用Ibinder的方法時,可能在同一時間從線程池的幾個線程中執行方法,因此必須是線程安全的。
從其它進程操作content provider的 query()insert()delete(),update(), and getType()時,也會遇到這種情況。

進程間通信

Andorid提供了進程間通信(IPC)機制,使用遠程過程調用(RPCs),通過在應用程序組件中調用但是在遠程的進程(另一個進程)中執行,並返回一個結果給調用者。這需要分解一個方法和它的數據到操作系統可以理解的層次,並把它從當前進程和地址空間傳到遠程的進程和地址空間,然後在那裏重組和重現。返回值將按照相反的方向傳回。Andorid提供了所有可以執行這些IPC交易的代碼,這樣就可以專注與定義和實現RPC編程接口。

執行IPC,應用程序必須綁定到一個service,使用bindService。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章