Android 包括一個應(yīng)用程序框架、幾個應(yīng)用程序庫和一個基于 Dalvik 虛擬機(jī)的運行時,所有這些都運行在 Linux 內(nèi)核之上。
通過利用 Linux 內(nèi)核的優(yōu)勢,Android 得到了大量操作系統(tǒng)服務(wù),包括進(jìn)程和內(nèi)存管理、網(wǎng)絡(luò)堆棧、驅(qū)動程序、硬件抽象層、安全性等相關(guān)的服務(wù)。
有關(guān)Java虛擬機(jī)跟進(jìn)程,線程的關(guān)系請參看下面這篇文章:
進(jìn)程、線程與JVM、CLR
http://blog.csdn.net/ghj1976/archive/2010/04/13/5481038.aspx
下面這篇文章對Android的進(jìn)程和線程描述的很好,我在這篇文章基礎(chǔ)補充了一些圖片和信息。
http://blog.csdn.net/L_serein/archive/2011/03/22/6269270.aspx
在安裝Android應(yīng)用程序的時候,Android會為每個程序分配一個Linux用戶ID,并設(shè)置相應(yīng)的權(quán)限,這樣其它應(yīng)用程序就不能訪問此應(yīng)用程序所擁有的數(shù)據(jù)和資源了。
在 Linux 中,一個用戶ID 識別一個給定用戶;在 Android 上,一個用戶ID 識別一個應(yīng)用程序。
應(yīng)用程序在安裝時被分配用戶 ID,應(yīng)用程序在設(shè)備上的存續(xù)期間內(nèi),用戶ID 保持不變。
默認(rèn)情況下,每個apk運行在它自己的Linux進(jìn)程中。當(dāng)需要執(zhí)行應(yīng)用程序中的代碼時,Android會啟動一個jvm,即一個新的進(jìn)程來執(zhí)行,因此不同的apk運行在相互隔離的環(huán)境中。
下圖顯示了:兩個 Android 應(yīng)用程序,各自在其自己的基本沙箱或進(jìn)程上。他們是不同的Linux user ID。
開發(fā)者也可以給兩個應(yīng)用程序分配相同的linux用戶id,這樣他們就能訪問對方所擁有的資源。
為了保留系統(tǒng)資源,擁有相同用戶id的應(yīng)用程序可以運行在同一個進(jìn)程中,共享同一個jvm。
如下圖,顯示了兩個 Android 應(yīng)用程序,運行在同一進(jìn)程上。
不同的應(yīng)用程序可以運行在相同的進(jìn)程中。要實現(xiàn)這個功能,首先必須使用相同的私鑰簽署這些應(yīng)用程序,然后必須使用 manifest 文件給它們分配相同的 Linux 用戶 ID,這通過用相同的值/名定義 manifest 屬性 android:sharedUserId 來做到。
Android進(jìn)程知識的補充:
下圖是標(biāo)準(zhǔn)的Android 架構(gòu)圖,
其中我們可以看到在“Android本地庫 & Java運行環(huán)境層”中,Android 運行時中,
Dalvik是Android中的java虛擬機(jī),可支持同時運行多個虛擬機(jī)實例;每個Android應(yīng)用程序都在自己的進(jìn)程中運行,都擁有一個獨立的Dalvik虛擬機(jī)實例;
所有java類經(jīng)過java編譯器編譯,然后通過SDK中的dx工具轉(zhuǎn)成.dex格式交由虛擬機(jī)執(zhí)行。
Android系統(tǒng)進(jìn)程
init進(jìn)程(1號進(jìn)程),父進(jìn)程為0號進(jìn)程,執(zhí)行根目錄底下的init可執(zhí)行程序,是用戶空間進(jìn)程
——-> /system/bin/sh
——-> /system/bin/mediaserver
——-> zygote
—————–> system_server
—————–>com.android.phone
—————–>android.process.acore(Home)
… …
kthreadd進(jìn)程(2號進(jìn)程),父進(jìn)程為0號進(jìn)程,是內(nèi)核進(jìn)程,其他內(nèi)核進(jìn)程都是直接或者間接以它為父進(jìn)程
當(dāng)一個程序第一次啟動時,Android會同時啟動一個對應(yīng)的主線程(Main Thread),主線程主要負(fù)責(zé)處理與UI相關(guān)的事件,如:用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,并把相關(guān)的事件分發(fā)到對應(yīng)的組件進(jìn)行處理。所以主線程通常又被叫做UI線程。
在開發(fā)Android 應(yīng)用時必須遵守單線程模型的原則: Android UI操作并不是線程安全的并且這些操作必須在UI線程中執(zhí)行。
如果在非UI線程中直接操作UI線程,會拋出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,這與普通的java程序不同。
由于UI線程負(fù)責(zé)事件的監(jiān)聽和繪圖,因此,必須保證UI線程能夠隨時響應(yīng)用戶的需求,UI線程里的操作應(yīng)該向中斷事件那樣短小,費時的操作(如網(wǎng)絡(luò)連接)需要另開線程,否則,如果UI線程超過5s沒有響應(yīng)用戶請求,會彈出對話框提醒用戶終止應(yīng)用程序。
如果在新開的線程中需要對UI進(jìn)行設(shè)定,就可能違反單線程模型,因此android采用一種復(fù)雜的Message Queue機(jī)制保證線程間通信。
Message Queue是一個消息隊列,用來存放通過Handler發(fā)布的消息。Android在第一次啟動程序時會默認(rèn)會為UI thread創(chuàng)建一個關(guān)聯(lián)的消息隊列,可以通過Looper.myQueue()得到當(dāng)前線程的消息隊列,用來管理程序的一些上層組件,activities,broadcast receivers 等等。你可以在自己的子線程中創(chuàng)建Handler與UI thread通訊。
通過Handler你可以發(fā)布或者處理一個消息或者是一個Runnable的實例。每個Handler都會與唯一的一個線程以及該線程的消息隊列管理。
Looper扮演著一個Handler和消息隊列之間通訊橋梁的角色。程序組件首先通過Handler把消息傳遞給Looper,Looper把消息放入隊列。Looper也把消息隊列里的消息廣播給所有的Handler,Handler接受到消息后調(diào)用handleMessage進(jìn)行處理。
實例如下:
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(); //得到當(dāng)前線程的Looper實例,由于當(dāng)前線程是UI線程也可以通過Looper.getMainLooper()得到 messageHandler = new MessageHandler(looper); //此處甚至可以不需要設(shè)置Looper,因為 Handler默認(rèn)就使用當(dāng)前線程的Looper }public void onClick(View v) {new Thread() {public void run() {Message message = Message.obtain();message.obj = "abc";messageHandler.sendMessage(message); //發(fā)送消息 }}.start();}Handler messageHandler = new Handler {public MessageHandler(Looper looper) {super(looper);}public void handleMessage(Message msg) {setTitle((String) msg.obj);}}
對于這個實例,當(dāng)這個activity執(zhí)行玩oncreate,onstart,onresume后,就監(jiān)聽UI的各種事件和消息。
當(dāng)我們點擊一個按鈕后,啟動一個線程,線程執(zhí)行結(jié)束后,通過handler發(fā)送一個消息,由于這個handler屬于UI線程,因此這個消息也發(fā)送給UI線程,然后UI線程又把這個消息給handler處理,而這個handler是UI線程創(chuàng)造的,他可以訪問UI組件,因此,就更新了頁面。
由于通過handler需要自己管理線程類,如果業(yè)務(wù)稍微復(fù)雜,代碼看起來就比較混亂,因此android提供了AsyncTask類來解決此問題。
首先繼承一下此類,實現(xiàn)以下若干方法,
onPreExecute(), 該方法將在執(zhí)行實際的后臺操作前被UI thread調(diào)用。可以在該方法中做一些準(zhǔn)備工作,如在界面上顯示一個進(jìn)度條。
doInBackground(Params...), 將在onPreExecute 方法執(zhí)行后馬上執(zhí)行,該方法運行在后臺線程中。這里將主要負(fù)責(zé)執(zhí)行那些很耗時的后臺計算工作。
可以調(diào)用publishProgress方法來更新實時的任務(wù)進(jìn)度。該方法是抽象方法,子類必須實現(xiàn)。
onProgressUpdate(Progress...),在publishProgress方法被調(diào)用后,UI thread將調(diào)用這個方法從而在界面上展示任務(wù)的進(jìn)展情況,例如通過一個進(jìn)度條進(jìn)行展示。
onPostExecute(Result), 在doInBackground 執(zhí)行完成后,onPostExecute 方法將被UI thread調(diào)用,后臺的計算結(jié)果將通過該方法傳遞到UI thread.
使用時需要遵循以下規(guī)則:
1)Task的實例必須在UI thread中創(chuàng)建
2)execute方法必須在UI thread中調(diào)用
3)不要手動的調(diào)用這些方法,只調(diào)用execute即可
4)該task只能被執(zhí)行一次,否則多次調(diào)用時將會出現(xiàn)異常
示例如下:
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進(jìn)程和線程模型
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/