Android的進程、線程模型

Android 包括一個應用程序框架、幾個應用程序庫和一個基於 Dalvik 虛擬機的運行時,所有這些都運行在 Linux 內核之上。

通過利用 Linux 內核的優勢,Android 得到了大量操作系統服務,包括進程和內存管理、網絡堆棧、驅動程序、硬件抽象層、安全性等相關的服務。

 

有關Java虛擬機跟進程,線程的關係請參看下面這篇文章:

進程、線程與JVM、CLR 
http://blog.csdn.net/ghj1976/archive/2010/04/13/5481038.aspx

 

下面這篇文章對Android的進程和線程描述的很好,我在這篇文章基礎補充了一些圖片和信息。

http://blog.csdn.net/L_serein/archive/2011/03/22/6269270.aspx 

android進程模型:

在安裝Android應用程序的時候,Android會爲每個程序分配一個Linux用戶ID,並設置相應的權限,這樣其它應用程序就不能訪問此應用程序所擁有的數據和資源了。

在 Linux 中,一個用戶ID 識別一個給定用戶;在 Android 上,一個用戶ID 識別一個應用程序。

應用程序在安裝時被分配用戶 ID,應用程序在設備上的存續期間內,用戶ID 保持不變。

 

默認情況下,每個apk運行在它自己的Linux進程中。當需要執行應用程序中的代碼時,Android會啓動一個jvm,即一個新的進程來執行,因此不同的apk運行在相互隔離的環境中。

下圖顯示了:兩個 Android 應用程序,各自在其自己的基本沙箱或進程上。他們是不同的Linux user ID。

image

開發者也可以給兩個應用程序分配相同的linux用戶id,這樣他們就能訪問對方所擁有的資源。

爲了保留系統資源,擁有相同用戶id的應用程序可以運行在同一個進程中,共享同一個jvm。

如下圖,顯示了兩個 Android 應用程序,運行在同一進程上。

不同的應用程序可以運行在相同的進程中。要實現這個功能,首先必須使用相同的私鑰簽署這些應用程序,然後必須使用 manifest 文件給它們分配相同的 Linux 用戶 ID,這通過用相同的值/名定義 manifest 屬性 android:sharedUserId 來做到。

image

Android進程知識的補充:

下圖是標準的Android 架構圖,

其中我們可以看到在“Android本地庫 & Java運行環境層”中,Android 運行時中,

Dalvik是Android中的java虛擬機,可支持同時運行多個虛擬機實例;每個Android應用程序都在自己的進程中運行,都擁有一個獨立的Dalvik虛擬機實例; 
所有java類經過java編譯器編譯,然後通過SDK中的dx工具轉成.dex格式交由虛擬機執行。

image

Android系統進程

init進程(1號進程),父進程爲0號進程,執行根目錄底下的init可執行程序,是用戶空間進程 
——-> /system/bin/sh 
——-> /system/bin/mediaserver 
——-> zygote 
—————–> system_server 
—————–>com.android.phone 
—————–>android.process.acore(Home) 
… …

kthreadd進程(2號進程),父進程爲0號進程,是內核進程,其他內核進程都是直接或者間接以它爲父進程

  

Android的單線程模型

當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如:用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。

在開發Android 應用時必須遵守單線程模型的原則: Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。

如果在非UI線程中直接操作UI線程,會拋出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,這與普通的java程序不同。

由於UI線程負責事件的監聽和繪圖,因此,必須保證UI線程能夠隨時響應用戶的需求,UI線程裏的操作應該向中斷事件那樣短小,費時的操作(如網絡連接)需要另開線程,否則,如果UI線程超過5s沒有響應用戶請求,會彈出對話框提醒用戶終止應用程序。

如果在新開的線程中需要對UI進行設定,就可能違反單線程模型,因此android採用一種複雜的Message Queue機制保證線程間通信。

 

Message Queue:

Message Queue是一個消息隊列,用來存放通過Handler發佈的消息。Android在第一次啓動程序時會默認會爲UI thread創建一個關聯的消息隊列,可以通過Looper.myQueue()得到當前線程的消息隊列,用來管理程序的一些上層組件,activities,broadcast receivers 等等。你可以在自己的子線程中創建Handler與UI thread通訊。 

通過Handler你可以發佈或者處理一個消息或者是一個Runnable的實例。每個Handler都會與唯一的一個線程以及該線程的消息隊列管理。

Looper扮演着一個Handler和消息隊列之間通訊橋樑的角色。程序組件首先通過Handler把消息傳遞給Looper,Looper把消息放入隊列。Looper也把消息隊列裏的消息廣播給所有的Handler,Handler接受到消息後調用handleMessage進行處理。

實例如下:

public void onCreate(Bundle savedInstanceState) {  
   super.onCreate(savedInstanceState);  
   setContentView(R.layout.main);  
   editText = (EditText) findViewById(R.id.weather_city_edit);  
   Button button = (Button) findViewById(R.id.goQuery);  
   button.setOnClickListener(this);  

   Looper looper = Looper.myLooper();  //得到當前線程的Looper實例,由於當前線程是UI線程也可以通過Looper.getMainLooper()得到  
    messageHandler = new MessageHandler(looper);  //此處甚至可以不需要設置Looper,因爲 Handler默認就使用當前線程的Looper  
} 

public void onClick(View v) {  
   new Thread() {  
      public void run() {  
          Message message = Message.obtain();  
          message.obj = "abc";  
          messageHandler.sendMessage(message);  //發送消息 
       }  
   }.start();  
} 

Handler messageHandler = new Handler {  
   public MessageHandler(Looper looper) {  
      super(looper);  
  } 
   public void handleMessage(Message msg) {  
      setTitle((String) msg.obj);  
   } 
}

對於這個實例,當這個activity執行玩oncreate,onstart,onresume後,就監聽UI的各種事件和消息。

當我們點擊一個按鈕後,啓動一個線程,線程執行結束後,通過handler發送一個消息,由於這個handler屬於UI線程,因此這個消息也發送給UI線程,然後UI線程又把這個消息給handler處理,而這個handler是UI線程創造的,他可以訪問UI組件,因此,就更新了頁面。

由於通過handler需要自己管理線程類,如果業務稍微複雜,代碼看起來就比較混亂,因此android提供了AsyncTask類來解決此問題。

 

AsyncTask:

首先繼承一下此類,實現以下若干方法,

onPreExecute(), 該方法將在執行實際的後臺操作前被UI thread調用。可以在該方法中做一些準備工作,如在界面上顯示一個進度條。 

doInBackground(Params...), 將在onPreExecute 方法執行後馬上執行,該方法運行在後臺線程中。這裏將主要負責執行那些很耗時的後臺計算工作。

可以調用publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。 

onProgressUpdate(Progress...),在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。 

onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,後臺的計算結果將通過該方法傳遞到UI thread.

使用時需要遵循以下規則:

1)Task的實例必須在UI thread中創建 

2)execute方法必須在UI thread中調用 

3)不要手動的調用這些方法,只調用execute即可

4)該task只能被執行一次,否則多次調用時將會出現異常

示例如下:

public void onCreate(Bundle savedInstanceState) {  
       super.onCreate(savedInstanceState);  
       setContentView(R.layout.main);  
       editText = (EditText) findViewById(R.id.weather_city_edit);  
       Button button = (Button) findViewById(R.id.goQuery);  
       button.setOnClickListener(this);  
}  

public void onClick(View v) {  
       new GetWeatherTask().execute(“aaa”);  
} 

class GetWeatherTask extends AsyncTask<String, Integer, String> {  
    protected String doInBackground(String... params) {  
         return getWetherByCity(params[0]);  
    } 
    protected void onPostExecute(String result) {  
         setTitle(result);
    }  
}

 

參考資料: 
Android進程和線程模型 
http://blog.csdn.net/L_serein/archive/2011/03/22/6269270.aspx

Hello Android 第三版 (二) 
http://blog.csdn.net/cqwty/archive/2010/09/08/5870219.aspx

理解 Android 上的安全性 
http://www.ibm.com/developerworks/cn/xml/x-androidsecurity/

發佈了23 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章