【Android 官方文檔】翻譯Android官方文檔 Processes and Threads(五)

注:轉載請註明出處:
http://blog.csdn.net/u011669081/article/details/52077956

前言

這是Android官方文檔翻譯的第五篇了,一路下來很不容易,尤其是那些又臭又長的英文句子太難理解,很費功夫,不過堅持下來,卻有不一樣的感受,不僅僅是簡單的翻譯,更有意義的是自己將知識點重新梳理,自己概括了出來。比較重要的英文官方文檔我也會繼續翻譯下去,在這裏繼續分享出現~謝謝大家的支持~


當一個應用程序組件首次啓動,並且此時應用程序也沒有其他組件在運行,那麼Android系統就會給應用創建一個含有單線程的linux進程。默認情況下,來自相同應用中的所有組件都會運行在同一個進程和線程(也稱爲 “main”主線程) 。如果組件啓動時,已經有了應用的進程(應用的其他組件已經在運行),那麼組件就會在已經存在的進程和線程中啓動組件。另外,可以指定組件運行在其他進程中,那麼也可以爲任何進程創建額外的線程。

這裏我們討論進程和線程,如何在Android中運用。


進程
在默認情況下,一個應用程序中的所有組件是運行在同一個進程中的,一般情況下應用不會改變這樣的現狀。但是,如果需要指定某個組件運行到某個特定進程,這裏就需要用上manifest文件了。

manifest文件中的元素 、 、 和 都可以去定義 android:procress ,可以指定組件運行的進程。設置屬性可以實現每個組件在不同的進程中運行,或者說某幾個組件一同運行在一個進程中。設置屬性也可以讓不同的應用中的組件運行在同一個進程中,從而實現多個程序共享一個Linux 用戶ID,得到一樣的權限。

另外,元素也支持android:process屬性,用於指定所有組件的默認進程。

如果手機內存不足,可又有其它爲用戶提供更緊急服務的進程需要更多內存,那麼Android可能會決定關閉一個進程。在此進程中運行着的應用程序組件也會因此被銷燬。當需要再次工作時,會爲這些組件重新創建一個進程。

那麼怎樣決定去關閉哪個進程呢,Android系統會判斷這些進程對使用者的重要程度。例如,一個可見的Activity與不可見的相比,應當選擇關閉不可見的進程。這也就是說,某個進程是否被關閉,這要取決這個進程中的組件所處的狀態。


進程的生命週期

Android系統會盡量保持應用的進程不被殺死,不過當新建進程或者需要運行重要進程時,Android 可能需要清理點一些進程,來保證其他進程的正常運行。爲了判斷保留或者關閉某個進程,會參照進程中的組件以及這些組件的狀態來決定,Android系統把進程按照重要程序劃分。重要程度低的進行會先被關閉,然後接着是下一個,這是釋放系統資源所需的辦法。

按照重要性劃分,共有5個,如下列舉:

  • 前臺進程:
    用戶當前操作的進程,如下列舉:
    • 正在與用戶交互的Activity
    • 被正在與用戶交互的activity綁定的服務
    • 其中運行着“前臺”服務——服務以[startForeground()方式被調用。
    • 正在執行生命週期回調方法(onCreate()、onStart()或onDestroy())的服務
    • 正在執行onReceive()方法的BroadcastReceiver
      通常來說,關閉前臺進程在任何時候都是少數的,只有在內存嚴重不足,纔會被終止。一般情況下,Android設備到了影響用戶界面響應,纔會終止前臺進程。
  • 可見進程
    不包含前臺組件、不過任然會影響用戶在屏幕上所見內容的進程。滿足以下任一條件時,進程被認爲是可見的:
  • 運行着不在前臺的Activity,但是這個Activity任然是可見的(onPause()調用)。
  • 運行着被可見(或前臺)activity綁定的服務。

  • 服務進程
    進程中運行着由startService()方法啓動的服務。雖然服務進程不會直接和用戶所見內容有關聯,但是他們通常會去執行一些用戶關心的操作(例如如在後臺播放音樂,或者是從網絡下載數據),所以,除非內存不足以維持所有前臺、可見進程同時運行,否則不會清理服務進程,系統會保持服務進程的運行。

  • 後臺進程
    運行着目前用戶不可見activity(Activity對象的onStop()方法已被調用)的進程。一般情況下,這些進程對用戶體驗沒有直接的影響,系統可能在任意時間終止它們,爲了回收內存供前臺進程、可見進程及服務進程使用。通常會有很多後臺進程在運行,所以它們會被保存在一個LRU(最近最少使用算法)列表中,爲的是確保最近被用戶使用的activity最後一個被終止。如果一個Activity實現了生命週期方法,並保存了當前的狀態,則終止此類進程不會對用戶體驗產生可見的影響。因爲在用戶返回時,Activity會恢復所有可見的狀態。
  • 空進程
    它不包含然後應用程序組件的進程。通常保留這些進程的目的是爲了緩存,清理內存時,系統通常會清理掉空進程。

系統根據進程中目前活躍組件的重要程度,Android會給進程評估一個儘可能高的級別。

所以說,一個進程的級別可能會由於某個進程的依賴而被提高——爲其它進程提供服務的進程級別永遠不會低於使用此服務的進程。比如:如果A進程中的content provider爲進程B中的客戶端提供服務,或進程A中的服務被進程B中的組件所調用,則A進程至少被視爲與進程B同樣重要。

因爲運行服務的進程級別是高於後臺activity進程的,所以,如果activity需要啓動一個長時間運行的操作,則爲其啓動一個服務會比簡單地創建一個工作線程更好些——尤其是該操作時間比activity的生存期還要長的情況下。比如,一個activity要把圖片上傳至Web網站,就應該創建一個服務來執行之,即使用戶離開了此activity,上傳還是會在後臺繼續運行。不論activity發生什麼情況,使用服務可以保證操作至少擁有“服務進程”的優先級。同理,廣播接收器broadcast receiver也是使用服務來處理耗時任務的,而不是簡單地把它放入線程中。


線程
根據上面的描述,想要確保界面的響應能力,這個關鍵在於不可以阻塞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(); 
}

表面上看代碼非常簡潔,但是這違背了不阻塞UI線程的原則,上面的例子是在工作線程中而不是UI線程裏修改了ImageView,可能會導致不確定,不可預見的後果,而且找這種問題也是非常耗時的。

爲了解決上述問題,Android提供了幾種方法,從其他線程中訪問UI線程。下面列舉方法:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • 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來處理UI線程分發來的消息。不過,最好的辦法是使用異步任務類AsyncTask,它簡化了工作線程與UI線程的交互操作。

使用異步任務
異步任務允許異步的方式對用戶界面進行操作,它先阻塞工作線程,再在UI線程中呈現結果,整個過程中不需要對線程和Handler進行操作。
要使用異步任務,要先繼承AsyncTask並且去實現 doinBackground方法,對象將運行在一個後臺線程池中,所以就能安全地改變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); 
    } 
}

這樣線程UI是安全的,代碼也夠精簡。


線程安全的方法

在某些情況下,多個線程需要調用同一個方法,所以被調用的方法必須要是線程安全的。

能被遠程調用的方法—bound服務中的方法,這是必須線程安全的。如果對IBinder所實現方法的調用發起於IBinder所在進程的內部,那麼這個方法是執行在調用者的線程中的。但是,如果調用發起於其他進程,那麼這個方法將運行於線程池中選出的某個線程中(而不是運行於進程的UI線程中),該線程池由系統維護且位於IBinder所在的進程中。例如,即使一個服務的onBind()方法是從服務所在進程的UI線程中調用的,onBind()所返回對象中的方法(比如,執行了RPC方法的一個子類)仍會從線程池中的線程被調用。因爲一個服務可以有不止一個客戶端,所以同時可以有多個線程池與同一個IBinder方法相關聯。因此IBinder方法必須實現爲線程安全的。

類似地,內容提供者(content provider)也能接收來自其它進程的數據請求。儘管ContentResolver類、ContentProvider類隱藏了進程間通訊管理的細節,ContentProvider中響應請求的方法——query()、insert()、delete()、update()和getType()方法——是從ContentProvider所在進程的線程池中調用的,而不是進程的UI線程。因爲這些方法可能會從很多線程同時調用,它們也必須實現爲線程安全的。


進程間通訊
Android利用遠程過程調用(remote procedure call,RPC)提供了一種進程間通信(IPC)機制,使用這種機制,被activity或者其他應用程序的組件調用的方法將(在其他進程中)被遠程執行,而所有的結果將被返回給調用者。這就要求把方法調用及其數據分解到操作系統可以理解的程度,並將其從本地的進程和地址空間傳輸至遠程的進程和地址空間,然後在遠程進程中重新組裝並執行這個調用。執行後的返回值將被反向傳輸回來。Android提供了執行IPC事務所需的全部代碼,因此只要把注意力放在定義和實現RPC編程接口上即可。

要執行IPC,應用程序必須用bindService()綁定到服務上。
以上爲 Progress and Thread 翻譯~

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