第10章 后臺默默地勞動者----------探究服務(wù)android

? ? ? ? ? ? 記得在我上學的時候,iPHone是屬于少數(shù)人才擁有的稀有物品嗜憔,Android甚至還沒面世映之,那個時候全球的手機市場是有諾基亞統(tǒng)治著的,當時Symbian操作系統(tǒng)做的特別出色亿昏,因為比起一般手機,它可以支持后臺功能档礁。那個時候能夠一邊打著電話角钩、聽音樂、一般在后臺掛著qq是件非成肜剑酷的事情递礼。所以我也曾經(jīng)單純的認為,支持后臺的手機就是智能手機羹幸。

? ? ? ? ? ? 而如今脊髓,Symbian早已風光不再,Android和ios占據(jù)了大部分智能市場份額栅受,Windows Phone也占據(jù)了一小部分将硝,目前已是三分天下的局面恭朗。在這三大操作系統(tǒng)中iOs和windows Phone一開始是不支持后臺的,后來逐漸意思到后臺這個功能的重要性依疼,才加入了后臺的功能痰腮。而Android則是沿用了Symbian的老習慣,從一開始就支持后臺功能律罢,這使得應(yīng)用程序即使在關(guān)閉的情況下仍然可以在后臺繼續(xù)運行诽嘉。不管怎么說,后臺功能屬于四大組件之一弟翘,其重要程序不言而喻虫腋。

10.1 服務(wù)是什么?

? ? ? ? ? ? ?服務(wù)(Service)是Android中實現(xiàn)程序后臺運行的解決方案稀余,他非常適合去執(zhí)行那些不需要和用戶交互而且還需要長期運行的任務(wù)悦冀。服務(wù)的運行不依賴于任何用戶界面,即使程序被切換到后臺睛琳,或者用戶打開了另一個應(yīng)用程序盒蟆,服務(wù)仍然能夠保持正常運行。

? ? ? ? ? ? ?不過需要注意的是师骗,服務(wù)并不是運行在一個獨立的進程當中历等,而是依賴于創(chuàng)建服務(wù)時所在的應(yīng)程序進程。當某個應(yīng)用程序進程被殺掉時辟癌,所有依賴于該進程的服務(wù)也會停止運行寒屯。

? ? ? ? ? ? ?另外,也不要被服務(wù)的后臺概念所迷惑黍少,實際上服務(wù)并不會自動開啟現(xiàn)程寡夹,所有的代碼都是默認運行在主線程當中的。也就是說厂置,我們需要在服務(wù)的內(nèi)部手動創(chuàng)建子線程菩掏,并在這里執(zhí)行具體的任務(wù),否則就會有可能出現(xiàn)主線程被阻塞的情況昵济。那么本章的第一堂課智绸,我們就先來學習一下關(guān)于Android多線程編程的知識。

10.2 Android多線程編程

? ? ? ? ? ?熟悉Java的你访忿,對多線程編程一定不會陌生吧瞧栗。當我們需要執(zhí)行一些耗時操作,比如說法起一條網(wǎng)絡(luò)請求時醉顽,考慮到網(wǎng)速等其他原因沼溜,服務(wù)器未必會立刻響應(yīng)我們的請求平挑,如果不將這類操作放在子線程里去運行游添,就會導(dǎo)致主線程被阻塞系草,從而引向用戶對軟件的正常使用,那么就讓我們從縣城的基本用法開始學習吧唆涝!

10.2.1 ?線程的基本用法

? ? ? ? ? Android多線程變成其實并不比Java多線程編程特殊找都,基本都是使用相同的語法。比如說廊酣,定義一個需要新建一個類繼承自Thread能耻,然后重寫父類的run()方法,并在里面編寫耗時邏輯即可亡驰,如下所示:

? ? ? ? ? ? 那么該如何啟動這個線程啦晓猛?其實也很簡單,只需要New出MyThread的實例凡辱,然后調(diào)用它的start()方法戒职,這樣run方法中的代碼就會在子線程當中運行了,如下所示:

當然透乾,使用繼承的方式耦合性有點高洪燥,更多的時候我們都會選擇實現(xiàn)Runnable接口的方式來定義一個線程,如下所示:

? ? ? ? ? ?如果使用了這種寫法乳乌,啟動線程的方法也需要進行相應(yīng)的改變

? ? ? ? ? 可以看到捧韵,Thread的構(gòu)造函數(shù)接受一個Runable參數(shù),而我們new出的MyThread正是一個實現(xiàn)了Runable接口的對象汉操,所以可以直接將它傳入到Thead的構(gòu)造函數(shù)里再来。接著調(diào)用Thread的start()方法,run()方法中的代碼就會在子線程中運行了磷瘤。

當然你不想專門在定義一個類趨勢線Runnable接口其弊,也可以使用匿名類的方式,這種寫法更為常見膀斋,如圖所示:

? ? ? ? ? ? ?以上幾種線程的使用方式相信你都不會感到陌生梭伐,因為在Java中創(chuàng)建和啟動線程也是使用同樣的方式。了解了線程的基本用法后仰担,下面我們來看一下Android多線程與Java多線程編程有什么不同的地方糊识。

10.2.2 在子線程中更新UI

? ? ? ? ? ? ? 和許多其他的GUI庫一樣,Android的UI也是線程不安全的摔蓝。也就是說赂苗,如果想要更新應(yīng)用程序的UI元素,則必須在主線程里進行贮尉,否則就會出現(xiàn)異常拌滋。

? ? ? ? ? ? 眼見為實,讓我們通過一個具體的例子來驗證一下吧猜谚。新建一個AndroidThreadTest項目败砂,然后修改activity_main.xml中的代碼赌渣,如此所示:





? ? ? ? ? 布局文件中定義了兩個控件,TextView用于在屏幕的鄭重顯示一個Hello World 字符串昌犹,Button用于改變吧TextView中顯示的內(nèi)容坚芜,我們希望再點擊Button后可以把TextView中顯示的字符串改成Nice to meet you。

? ? ? ? ? ?接下來修改MainActivity中的代碼斜姥。如下所示:



? ? ? ? ? 可以看到鸿竖,我們在Change Text按鈕的點擊事件里面開啟了一個子線程,然后在子線程中調(diào)用TextView的setText()方法將現(xiàn)實的字符串改成Nice to meet you铸敏。代碼的邏輯非常簡單缚忧,只不過我們是在子線程中更新UI的。現(xiàn)在運行以下程序杈笔,并點擊Change Text按鈕搔谴,你會發(fā)現(xiàn)程序茍然崩潰了,如圖10.1所示:


然后觀察logCat中的的錯誤日志桩撮,可以看到由于在子線程中更新UI所導(dǎo)致的如圖10.2所示:


由此證實了敦第,Android確實是不允許在子線程中進行UI操作的問題。本小節(jié)中我們先來學習一下一步消息處理的使用方法店量,下一小節(jié)中再去分析它的原理芜果。

修改MainActivity中的代碼,如下所示:



? ? ? ? ? 這里我們先是定義了一個整形常量UPDATE_TEXT融师,用于表示更新TextView這個動作右钾。然后新增一個Handle對象,并重寫了父類的handleMessage()方法旱爆,在這里對具體的Message進行了處理舀射。如果發(fā)現(xiàn)Message的what字段的值等于UPDATE_TEXT,就將TextView現(xiàn)實的內(nèi)容改成Nice to meet you怀伦。

? ? ? ? ? 下面再來看一下Change Text按鈕的點擊事件中的代碼脆烟。可以看到房待,這次我們并沒有在子線程里直接進行UI操作邢羔,而是創(chuàng)建了Message(android.os.Message)對象,并將它的what字段的值指定為UPDATE_TEXT桑孩,然后調(diào)用Handle的endMessage()方法將這條Message放棄愛送出去拜鹤。很快,Handle救護收到這條Message流椒,并在handleMessage()方法中對它進行處理敏簿。注意此時handleMessage()方法中的代碼就是在主線程運行的了,所以我們可以放心的在這里進行UI操作。接下來對Message攜帶的what字段的值進行判斷惯裕,如果等去UPDATE_TEXT温数,就像TextView顯示的內(nèi)容改成Nice to meet you。如圖10.3所示:


? ? ? ? ? ?這樣你就已經(jīng)掌握了Android異步消息處理的基本用法轻猖,使用這種機制就可以出色的解決掉在子線程中更新UI的問題帆吻。不過恐怕你對他的工作原理還不是很清楚域那,下面我們就來分析一下Android異步消息處理機制到底是如何工作的咙边。

10.2.3 ? ? ? ?解析 消息異步處理機制

? ? ? ? ? ? ? ? ?Android中的異步消息處理主要是由4個部分組成:Message、Handle次员、MessageQueue和Looper败许。其中Message和Handle在上一小節(jié)中我們已經(jīng)接觸過了,而MessageQueue和Looper對于你來說還是全新的概念淑蔚,下面我就來對這4部分進行以下簡要的介紹:

1 Message

? ? ? ? ? ? ? ?Message是在線程之間傳遞的消息市殷,它可以在內(nèi)部攜帶少量的信息,用于在不同線程之間交換數(shù)據(jù)刹衫。上一小節(jié)中我們使用到了Message的what字段醋寝,除此之外還可以使用argl和argl2字段來攜帶一些整型數(shù)據(jù),使用Object對象带迟。

2 ?Handler

? ? ? ? ? ? Handler顧名思義也就是處理者的意思音羞,他主要是用于發(fā)送和處理消息的。發(fā)送消息一般是使用Handle的sendMessage()方法仓犬,而發(fā)出的消息經(jīng)過一系列的輾轉(zhuǎn)處理后嗅绰,最終會傳遞到Handler的handleMessage()方法中。

3 MessageQueue

? ? ? ? ? ?MessageQueue是消息隊列的意思搀继,他主要用于存放所有通過Handle發(fā)送的消息窘面。這部分消息會一直存在于消息隊列中,等待被處理叽躯。每個線程中只會有一個MessageQueue對象财边。

4 Looper

? ? ? ? ? ?Looper是每個線程中的MessageQueue的管家,調(diào)用Looper的loop()方法后点骑,就會進入到一個無限循環(huán)當中制圈,然后每當發(fā)現(xiàn)messageQueue中存在一條消息,就會將它取出畔况,并傳遞到Hnadle的handleMessage方法中鲸鹦、每個線程中也只會有一個Looper對象。

? ? ? ? ? 了解了Message跷跪、Handler馋嗜、MessageQueue、以及Looper的基本蓋簾后吵瞻,我們再來把異步消息 處理的整個流程梳理一遍葛菇。首先需要在主線程中創(chuàng)建一個Handle對象甘磨,并重寫handleMesage()方法。然后當子線程中需要進行UI操作時眯停,就創(chuàng)建一個Message對象济舆,并通過Handle將這條消息發(fā)送出去。之后這條消息會被添加到MessageQueue的隊列中等待被處理莺债,而Looper則會一直嘗試從MessageQueue中取出待處理的消息滋觉,最后分發(fā)回Hnadle的HnadleMessage()方法中。由于Hnadle是在主線程中創(chuàng)建的齐邦,所以此時handleMessage()中的代碼也會在主線程中運行椎侠,于是我們在這里就可以安心的進行UI操作了。整個異步消息處理的流程示意圖如圖10.4所示:



? ? ? ? ? ?一條Message經(jīng)過這樣一個流程的輾轉(zhuǎn)調(diào)用之后措拇,也就從子線程進入到了主線程我纪,從不能更新UI變成了可以更新UI,整個異步消息處理的核心思想就是如此丐吓。

? ? ? ? ? ?而我們在9.2.1小節(jié)中用到的runOnUiThread()方法其實就是一個異步消息處理機制的接口封裝浅悉,他雖然上看起來用法更簡單,但其實背后的實現(xiàn)原理和圖10.4中描述的是一模一樣的券犁。

10.2.4 ? ? ? 使用AsyncTack

? ? ? ? ? ?不過為了更方便我們在子線程中對UI進行操作术健,Android還提供了另一些好用的工具,比如AsyncTask族操。借助AsyncTask苛坚,即使你對異步消息處理機制完全不了解,也可以十分簡單的從子線程切換到主線程色难。當然AsyncTask背后的實現(xiàn)原理也是基于異步消息處理機制的泼舱,只是Android幫我們做了很好地封裝而已。

? ? ? ? ? ?首先看一下AsyncTask的基本用法枷莉,由于AsyncTask是一個抽象類娇昙,所以如果我們想使用它,就必須要創(chuàng)建一個類去繼承他笤妙。在繼承使我們可以為AsyncTask類指定3個泛型慘呼是冒掌,這三個參數(shù)的用途如下。

? ? ? Params: 在執(zhí)行AsyncTask時需要傳入的參數(shù)蹲盘,可用在后臺任務(wù)中使用股毫。

? ? ? Progress: 后臺任務(wù)執(zhí)行時,如果需要在界面上顯示當前的進度召衔,則使用這里指定的泛型作為進度單位铃诬。

? ? ? Result: 當任務(wù)執(zhí)行完畢后,如果需要對結(jié)果進行返回,則使用這里指定的泛型作為返回值類型趣席。

? ? ? 因此兵志,一個最簡單的自定義AsyncTask就可以寫成如下方式:

? ? ? ? ? ? ?這里我們把AsyncTask的第一個泛型參數(shù)指定為Void,表示在執(zhí)行AsyncTask的時候不需要傳入?yún)?shù)給后臺任務(wù)宣肚,想罕。第二個泛型參數(shù)指定為Integer,表示使用整形數(shù)據(jù)作為進度顯示單位霉涨。第三個泛型參數(shù)指定為Boolean按价,則表示使用布爾型數(shù)據(jù)來反饋執(zhí)行結(jié)果。

? ? ? ? ? ? ?當然嵌纲,目前我們自定義的DownloadTask還是一個空任務(wù)俘枫,并不能進行任何實際的操作腥沽,我們還需要去重寫AsyncTask中的幾個方法才能完成任務(wù)的定制逮走。經(jīng)常需要去重寫的方法還有以下4個。

1 onPreExcute()

? ? ? ? ? ? 這個方法會在后臺開始執(zhí)行任務(wù)之前調(diào)用料身,用于進行一些界面初始化操作樊破,比如顯示一個進度條對話框等

2 doInBackGround(Params)

? ? ? ? ? ?這個方法中的所有代碼都會在子線程中運行谢翎,我們應(yīng)該在這里去處理所有的耗時任務(wù)。任務(wù)一旦完成就可以通過return語句來將任務(wù)的執(zhí)行結(jié)果返回墓臭,如果AsyncTask的第三個參數(shù)指定的是Void,就可以不返回任務(wù)執(zhí)行結(jié)果妖谴。注意在這個方法里面是不可以進行UI操作的窿锉,如果需要更新UI元素,比如說返回當前任務(wù)的執(zhí)行進度膝舅,可以調(diào)用publisProgress(Progress...)方法來完成嗡载。

3 onProgressUpdata(Progress....)

? ? ? ? ? 當在后臺任務(wù)中調(diào)用publisProgress(Progress...)方法后,onProgressUpdata(Progress....)就會很快被調(diào)用仍稀,該方法中攜帶的參數(shù)就是后臺任務(wù)中傳遞過來的洼滚。在這個方法中可以對UI進行操作,利用參數(shù)中的數(shù)值就可以對界面元素進行相應(yīng)的更新技潘。

4. OnPostExecute(Result)

? ? ? ? ? 當后臺執(zhí)行完畢通過return語句進行返回時遥巴,這個方法就很快會被調(diào)用。反悔的數(shù)據(jù)作為參數(shù)傳遞到此方法中享幽,可以利用反悔的數(shù)據(jù)進行一些UI操作铲掐,比如說提醒任務(wù)執(zhí)行的結(jié)果,以及關(guān)閉掉進度條對話框等值桩。

? ? ? ? ? 因此摆霉,一個比較完整的自定義項目AsyncTask就可以寫成如下方式:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子斯入,更是在濱河造成了極大的恐慌砂碉,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刻两,死亡現(xiàn)場離奇詭異增蹭,居然都是意外死亡,警方通過查閱死者的電腦和手機磅摹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門滋迈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人户誓,你說我怎么就攤上這事饼灿。” “怎么了帝美?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵碍彭,是天一觀的道長。 經(jīng)常有香客問我悼潭,道長庇忌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任舰褪,我火速辦了婚禮皆疹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘占拍。我一直安慰自己略就,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布晃酒。 她就那樣靜靜地躺著表牢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掖疮。 梳的紋絲不亂的頭發(fā)上初茶,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音浊闪,去河邊找鬼恼布。 笑死,一個胖子當著我的面吹牛搁宾,可吹牛的內(nèi)容都是我干的折汞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盖腿,長吁一口氣:“原來是場噩夢啊……” “哼爽待!你這毒婦竟也來了损同?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鸟款,失蹤者是張志新(化名)和其女友劉穎膏燃,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體何什,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡组哩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了处渣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伶贰。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖罐栈,靈堂內(nèi)的尸體忽然破棺而出黍衙,到底是詐尸還是另有隱情,我是刑警寧澤荠诬,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布琅翻,位于F島的核電站,受9級特大地震影響浅妆,放射性物質(zhì)發(fā)生泄漏望迎。R本人自食惡果不足惜障癌,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一凌外、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涛浙,春花似錦康辑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至我注,卻和暖如春按咒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背但骨。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工励七, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奔缠。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓掠抬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親校哎。 傳聞我的和親對象是個殘疾皇子两波,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容