引言
當(dāng)應(yīng)用程序的組件第一次運(yùn)行時(shí),Android將啟動(dòng)一個(gè)只有一個(gè)執(zhí)行線程的Linux進(jìn)程。默認(rèn),應(yīng)用程序所有的組件運(yùn)行在這個(gè)進(jìn)程和線程中。然而,你可以安排組件運(yùn)行在其他進(jìn)程中,且你可以為進(jìn)程衍生出其它線程。本文從下面幾點(diǎn)來介紹Android的進(jìn)程與線程:
- 1、進(jìn)程
-
2、線程
- 2.1、遠(yuǎn)程過程調(diào)用(Remote procedure calls,RPCs)
- 2.2、線程安全方法
1、進(jìn)程
組件運(yùn)行于哪個(gè)進(jìn)程中由清單文件控制。組件元素—— <activity> 、 <service> 、 <receiver> 、 <provider> ,都有一個(gè) process 屬性可以指定組件運(yùn)行在哪個(gè)進(jìn)程中。這個(gè)屬性可以設(shè)置為每個(gè)組件運(yùn)行在自己的進(jìn)程中,或者某些組件共享一個(gè)進(jìn)程而其他的不共享。他們還可以設(shè)置為不同應(yīng)用程序的組件運(yùn)行在同一個(gè)進(jìn)程中——假設(shè)這些應(yīng)用程序共享同一個(gè)Linux用戶ID且被分配了同樣的權(quán)限。 <application> 元素也有 process 屬性,為所有的組件設(shè)置一個(gè)默認(rèn)值。
所有的組件都在特定進(jìn)程的主線程中實(shí)例化,且系統(tǒng)調(diào)用組件是由主線程派遣。不會(huì)為每個(gè)實(shí)例創(chuàng)建單獨(dú)的線程,因此,對(duì)應(yīng)這些調(diào)用的方法——諸如 View.onKeyDown() 報(bào)告用用戶的行為和生命周期通知,總是運(yùn)行在進(jìn)程的主線程中。這意味著,沒有組件當(dāng)被系統(tǒng)調(diào)用時(shí)應(yīng)該執(zhí)行很長時(shí)間或阻塞操作(如網(wǎng)絡(luò)操作或循環(huán)計(jì)算),因?yàn)檫@將阻塞進(jìn)程中的其它組件。你可以為長操作衍生獨(dú)立的線程。
public boolean onKeyDown (int keyCode,KeyEvent event):默認(rèn)實(shí)現(xiàn)KeyEvent.Callback.onKeyMultiple(),當(dāng)按下視圖的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后釋放時(shí)執(zhí)行,如果視圖可用且可點(diǎn)擊。
參數(shù)
keyCode -表示按鈕被按下的鍵碼,來自KeyEvent
event -定義了按鈕動(dòng)作的KeyEvent對(duì)象返回值
如果你處理事件,返回true;如果你想下一個(gè)接收者處理事件,返回false。
當(dāng)內(nèi)存剩余較小且其它進(jìn)程請(qǐng)求較大內(nèi)存并需要立即分配,Android要回收某些進(jìn)程,進(jìn)程中的應(yīng)用程序組件會(huì)被銷毀。當(dāng)他們?cè)俅芜\(yùn)行時(shí),會(huì)重新開始一個(gè)進(jìn)程。
當(dāng)決定終結(jié)哪個(gè)進(jìn)程時(shí),Android會(huì)權(quán)衡他們對(duì)用戶重要性的相對(duì)權(quán)值。例如,與運(yùn)行在屏幕可見的活動(dòng)進(jìn)程相比(前臺(tái)進(jìn)程),它更容易關(guān)閉一個(gè)進(jìn)程,它的活動(dòng)在屏幕是不可見(后臺(tái)進(jìn)程)。決定是否終結(jié)進(jìn)程,取決于運(yùn)行在進(jìn)程中的組件狀態(tài)。關(guān)于組件的狀態(tài),將在后面一篇—— 組件生命周期 中介紹。
2、線程
雖然你可能會(huì)將你的應(yīng)用程序限制在一個(gè)進(jìn)程中,但有時(shí)候你會(huì)需要衍生一個(gè)線程做一些后臺(tái)工作。因?yàn)橛脩艚缑姹仨毢芸斓仨憫?yīng)用戶的操作,所以活動(dòng)寄宿的線程不應(yīng)該做一些耗時(shí)的操作如網(wǎng)絡(luò)下載。任何不可能在短時(shí)間完成的操作應(yīng)該分配到別的線程。
線程在代碼中是用標(biāo)準(zhǔn)的Java線程對(duì)象創(chuàng)建的,Android提供了一些方便的類來管理線程—— Looper 用于在線程中運(yùn)行消息循環(huán)、 Handler 用戶處理消息、 HandlerThread 用戶設(shè)置一個(gè)消息循環(huán)的線程。
Looper類
該類用戶在線程中運(yùn)行消息循環(huán)。線程默認(rèn)沒有消息循環(huán),可以在線程中調(diào)用prepare()創(chuàng)建一個(gè)運(yùn)行循環(huán);然后調(diào)用loop()處理消息直到循環(huán)結(jié)束。大部分消息循環(huán)交互是通過Handler類。下面是一個(gè)典型的執(zhí)行一個(gè)Looper線程的例子,分別使用prepare()和loop()創(chuàng)建一個(gè)初始的Handler與Looper交互:
? class LooperThread extends Thread {
????? public Handler mHandler;
?????
????? public void run() {
????????? Looper.prepare();
?????????
????????? mHandler = new Handler() {
????????????? public void handleMessage(Message msg) {
????????????????? // process incoming messages here
????????????? }
????????? };
?????????
????????? Looper.loop();
????? }
? }
更多的關(guān)于 Looper 的信息及 Handler、 HandlerThread 請(qǐng)參閱相關(guān)資料。
2.1、遠(yuǎn)程過程調(diào)用(Remote procedure calls,RPCs)
Android有一個(gè)輕量級(jí)的遠(yuǎn)程過程調(diào)用機(jī)制——方法在本地調(diào)用卻在遠(yuǎn)程(另外一個(gè)進(jìn)程中)執(zhí)行,結(jié)果返回給調(diào)用者。這需要將方法調(diào)用和它伴隨的數(shù)據(jù)分解為操作系統(tǒng)能夠理解的層次,從本地進(jìn)程和地址空間傳輸?shù)竭h(yuǎn)程進(jìn)程和地址空間,并重新組裝調(diào)用。返回值以相反方向傳輸。Android提供了做這些工作的所有代碼,這樣我們可以專注于定義和執(zhí)行RPC接口本身。
一個(gè)RPC接口僅包含方法。所有的方法同步地執(zhí)行(本地方法阻塞直到遠(yuǎn)程方法執(zhí)行完成),即使是沒有返回值。簡言之,該機(jī)制工作原理如下:首先,你用簡單的IDL(interface definition language,接口定義語言)聲明一個(gè)你想實(shí)現(xiàn)的RPC接口。從這個(gè)聲明中, aidl 工具生成一個(gè)Java接口定義,提供給本地和遠(yuǎn)程進(jìn)程。它包含兩個(gè)內(nèi)部類,如下圖所示:
內(nèi)部類有管理你用IDL定義的接口的遠(yuǎn)程過程調(diào)用所需要的所有代碼。這兩個(gè)內(nèi)部類都實(shí)現(xiàn)了 IBinder 接口。其中之一就是在本地由系統(tǒng)內(nèi)部使用,你寫代碼可以忽略它。另外一個(gè)是 Stub ,擴(kuò)展自 Binder 類。除了用于有效地IPC(interprocess communication)調(diào)用的內(nèi)部代碼,內(nèi)部類在RPC接口聲明中還包含方法聲明。你可以定義 Stub 的子類實(shí)現(xiàn)這些方法,如圖中所示。
通常情況下,遠(yuǎn)程過程有一個(gè)服務(wù)管理(因?yàn)榉?wù)能通知系統(tǒng)關(guān)于進(jìn)程和它連接的其它進(jìn)程的信息)。它有由 aidl 工具生成的接口文件和 Stub 子類實(shí)現(xiàn)的RPC方法。服務(wù)的客戶端僅有由 aidl 工具生成的接口文件。
下面介紹服務(wù)如何與它的客戶端建立連接:
- 服務(wù)的客戶端(在本地端的)應(yīng)該實(shí)現(xiàn) onServiceConnected() 和 onServiceDisconnected() 方法,因此當(dāng)與遠(yuǎn)程服務(wù)建立連接成功和斷開連接是會(huì)通知它。然后調(diào)用 bindService() 建立連接。
- 服務(wù)的onBind()方法將實(shí)現(xiàn)為接受或拒絕連接,者取決于它接受到的意圖(該意圖傳送到binServive())。如果連接被接受,它返回一個(gè) Stub 子類的實(shí)例。
- 如果服務(wù)接受連接,Android調(diào)用客戶端的onServiceConnected()方法且傳遞給它一個(gè)IBinder對(duì)象,返回由服務(wù)管理的 Stub 子類的一個(gè)代理。通過代理,客戶端可以調(diào)用遠(yuǎn)程服務(wù)。
這里只是簡單地描述,省略了一些RPC機(jī)制的細(xì)節(jié)。你可以查閱相關(guān)資料或繼續(xù)關(guān)注Android開發(fā)之旅,后面將為你奉上。
2.2、線程安全方法
在一些情況下,你實(shí)現(xiàn)的方法可能會(huì)被不止一個(gè)線程調(diào)用,因此必須寫成線程安全的。這對(duì)遠(yuǎn)程調(diào)用方法是正確的——如上一節(jié)討論的RPC機(jī)制。當(dāng)從 IBinder 進(jìn)程中調(diào)用一個(gè) IBinder 對(duì)象中實(shí)現(xiàn)的一個(gè)方法,這個(gè)方法在調(diào)用者的線程中執(zhí)行。然而,當(dāng)從別的進(jìn)程中調(diào)用,方法將在Android維護(hù)的 IBinder 進(jìn)程中的線程池中選擇一個(gè)執(zhí)行,它不在進(jìn)程的主線程中執(zhí)行。例如,一個(gè)服務(wù)的 onBind() 方法在服務(wù)進(jìn)程的主線程中被調(diào)用,在 onBind() 返回的對(duì)象中執(zhí)行的方法(例如,實(shí)現(xiàn)RPC方法的 Stub 子類)將在線程池中被調(diào)用。由于服務(wù)可以有一個(gè)以上的客戶端,所以同時(shí)可以有一個(gè)以上的線程在執(zhí)行同一個(gè) IBinder 方法。因此, IBinder 的方法必須是線程安全的。
同樣,一個(gè)內(nèi)容提供者可以接受其它進(jìn)程產(chǎn)生的數(shù)據(jù)請(qǐng)求。雖然 ContentResolver 和 ContentProvider 類隱藏進(jìn)程通信如何管理的,對(duì)應(yīng)哪些請(qǐng)求的 ContentResolver 方法——query()、insert()、delete()、update()、getType(),在內(nèi)容提供者的進(jìn)程的線程池中被調(diào)用,而不是在這一進(jìn)程的主線程中。因?yàn)檫@些方法可以同時(shí)從任意數(shù)量的線程中調(diào)用,他們也必須實(shí)現(xiàn)為線程安全的。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
