知識點乾貨--聊一聊Android中Service與Thread的區別

古語說得好:“一寸光陰一寸金,寸金難買寸光陰。”一寸光陰和一寸長的黃金一樣昂貴,而一寸長的黃金卻難以買到一寸光陰。比喻時間十分寶貴。此語句出自唐朝王貞白的《白鹿洞二首》,“讀書不覺已春深, 一寸光陰一寸金。不是道人來引笑, 周情孔思正追尋。”警示我們一寸光陰一寸金, 寸金難買寸光陰。我們應珍惜時光,而不可虛度年華。
又道是“少壯不努力,老大徒傷悲”,告訴我們不要虛度青春,古往今來,有許許多多人,當他們到了白髮蒼蒼的年紀,讀到這首詩的時候,都會爲之深深震撼,所以它纔會被廣爲傳誦,並且流傳到今天。大教育家孔子也曾經指出:“四十五十而無聞焉,斯亦不足畏也已。”他的意思是說,如果人到了40歲,頂多50歲,還沒在社會上做成點事,他一輩子也就差不多是這樣了。也正因爲如此,民族英雄岳飛在他的名作《滿江紅》裏運用了這格言來表達他對人生的態度:“三十功名塵與土,八千里路雲和月。莫等閒、白了少年頭,空悲切。”他更以自己短暫而光輝的一生,給予後人有力的鼓舞和鞭策。
所以大家都應該珍惜時間,不要枉度青春。

對於Android初學者來說,常常分不清什麼時候使用Service,什麼時候使用Thread,能用Thread解決問題時,往往會啓動一個Service來幹活;而需要使用Service才能更好的解決問題時,還在堅持使用Thread,導致出現很多影響性能,以及程序執行效率的問題。今天我們就聊一聊Service與Thread到底是什麼,區別在哪,使用場景等問題,使不熟悉它們的同學能夠更好的掌握這個知識點,避免以後錯誤的使用。大家準備好,老司機要開車了,有暈車的沒有?如果有請準備好暈車藥,車速可能會快一些,還有些顛簸,扶好坐好,我們開車了。

其實二者沒有半毛錢的關係 。

1、Service是什麼?

(1)、定義

Service 是Android的四大組件之一。當它定義的時候,如果在AndroidManifest.xml中註冊時,未指定android:process屬性,那它就是一個Local Service,對應的 Service和應用程序運行於一個進程中,這個進程的名稱使用應用的包名來表示,如,包名爲“xxx.xxx.xxx”,則進程名稱也是“xxx.xxx.xxx”,並且運行在main 線程(或者叫主線程,UI線程,我們本文統一叫main線程)中。如:onCreate,onStart 這些函數在被系統調用的時候都是在 main 線程上運行的。如果在AndroidManifest.xml中未指定android:process屬性,比如,定義android:process=”:xxxService”這樣設置後,那它就是一個Remote Service,它運行於一個獨立的進程中,進程名稱爲“包名:xxxService”,並且它對應的 Service 也是運行在這個獨立進程的 main 線程上。也就是說無論是Local Service或者Remote Service,它始終是運行在main線程上的。
這裏有個知識點,就是android:process屬性,在定義Remote Service時,往往有兩種方式,
一種是例如這樣,android:process=”:xxxService”,那它的進程名將是“包名:xxxService”。
還有一種是這樣定義,android:process=”yyy.yyy,yyy.yyyService”,也就是進程名隨意寫,但是必須是小寫字母,一般我們這樣定義,“包名.yyyService”。
那這兩種定義有什麼區別呢?
前者包含一個冒號,並且以當前包名打頭,表示這個新的進程對於這個應用來說是私有的,其他應用應該是無法訪問或者和它進行交互的。而後者這個服務進程將運行在全局進程中,對於所有應用是開放的,這將允許在其它應用中可以通過一些手段來訪問它提供的功能。這就是二者的區別,所以一般如果不對外提供服務,並且爲了安全性更好一些,建議使用帶冒號的方式。

(2)、Service生命週期

Service作爲Android的四大組件之一,和Activity相同,是有生命週期的,它一般被看成運行於後臺,沒有界面的“Activity”。
它的生命週期包含如下方法,
● onCreate
創建服務
● onStart(或onStartCommand)
開始服務
● onDestroy
銷燬服務
● onBind
綁定服務
● onUnbind
解綁服務

啓動或者綁定服務對應的方法有,
● startService()
啓動服務
使用startService方式啓動服務。
如果一個Service被某個Activity 使用 startService 方法啓動,那麼不管是否有Activity使用bindService綁定或unbindService解除綁定到該Service,該Service都在後臺運行,但它依舊運行於main線程。如果這個Service被startService方法多次啓動,那麼onCreate方法只會調用一次,onStart將會被調用多次(對應調用startService的次數),並且系統只會創建Service的一個實例。該Service將會一直在後臺運行,即使啓動它的Activity已經退出,它直到某個activity調用stopService,或自身調用的stopSelf方法纔會結束服務。當然如果系統資源不足,android系統也可能結束服務。

● bindService()
綁定服務
使用bindService方式啓動服務。
如果一個Service被某個Activity 調用 bindService 方法綁定啓動,不管調用 bindService 調用幾次,onCreate方法都只會調用一次,同時onStart方法不會被調用。當連接建立之後,Service將會一直運行,除非調用unbindService 方法斷開連接或者之前調用bindService 的 Context 不存在了(如Activity被finish的時候),系統纔會自動停止Service,對應onDestroy將被調用。

還有一中複雜用法,就是同時使用startService和bindService方式啓動服務。如果一個Service又被啓動又被綁定,則該Service將會一直在後臺運行。並且不管如何調用,onCreate始終只會調用一次,對應startService調用多少次,Service的onStart便會調用多少次。調用unbindService將不會停止Service,而必須由外界的activity調用 stopService 或 Service自身調用stopSelf 來停止服務。

另外,還有停止服務和解除綁定服務的方法,
● stopService()
關閉服務

● unbindService()
解綁服務

2、Thread是什麼?

(1)、定義

Thread 是程序執行的最小單元,它是分配CPU的基本單位。可以用 Thread 來執行一些異步的操作。而Thread分爲main線程和子線程。我們通常做的費時操作都會放到子線程中,而和UI刷新的功能都放到了main線程中。

(2)、Thread生命週期

線程是一個動態執行的過程,它也有一個從產生到死亡的過程。

它的生命週期有五種狀態:
● 新建(new Thread)
當創建Thread類的一個實例時,此線程進入新建狀態或未啓動狀態。
例如:Thread thread=new Thread();

● 就緒(runnable)
線程已經被啓動,正在等待被分配給CPU時間片,也就是說此時線程正在就緒隊列中排隊等候得到CPU資源。
例如:thread.start();

● 運行(running)
線程獲得CPU資源正在執行任務,正在運行run()方法,此時除非此線程自動放棄CPU資源或者有優先級更高的線程進入,否則線程將一直運行到結束。

● 死亡(dead)
當線程執行完畢或被其它線程殺死,線程就進入死亡狀態,這時線程不可能再進入就緒狀態等待執行。
自然終止:正常運行run()方法後終止
異常終止:調用stop()方法讓一個線程終止運行

● 堵塞(blocked)
由於某種原因導致正在運行的線程讓出CPU並暫停自己的執行,即進入堵塞狀態。
正在睡眠:用sleep(long t) 方法可使線程進入睡眠方式。一個睡眠着的線程在指定的時間過去可進入就緒狀態。
正在等待:調用wait()方法。(在此狀態下,調用motify()方法線程會回到就緒狀態)
被另一個線程所阻塞:調用suspend()方法。(在此狀態下,調用resume()方法恢復)

常用的方法有:
● void run()
創建該類的子類時必須實現此方法。

● void start()
啓動線程

● static void sleep(long t)
釋放CPU的執行權t秒,但不釋放鎖。

● final void wait()
釋放CPU的執行權,釋放鎖

● static void yied()
可以對當前線程進行臨時暫停(讓線程將資源釋放出來)

3、使用場景

由以上定義可見,兩者並沒有什麼關係。先說一下爲什麼會有Service這個組件,爲什麼會有它和Thread自身缺點有關係,因爲只使用Thread無法解決問題。我們知道他們兩個都是在後臺執行任務,都沒有界面。
我們在使用Thread啓動一個子線程來幹一些費時的操作時,這個子線程一般是由Activity來啓動執行的(假如說沒有Service組件),而Thread子線程 的運行是獨立於 Activity 的,那麼當一個 Activity 被 finish 之後,如果你沒有主動停止 Thread 或者 Thread 裏的 run 方法沒有執行完畢的話,Thread 並不會結束,它會一直執行下去。因此這裏會出現一個問題:當 Activity 被 finish 之後,你不再持有該 Thread 的引用,你就無法對其進行控制。
另一方面,你也沒有辦法在不同的 Activity 中對同一Thread 進行控制。
舉個例子:如果你的 Thread 需要不停地過一段時間就要連接服務器一次做某種同步,該 Thread 需要在 Activity 退出後也在運行。這個時候你就沒有辦法在您啓動的 Activity 裏面控制之前Activity創建的 Thread,這樣這個子線程就相當於一個“野線程”了,你無法對其進行狀態監聽和控制。
那這個時候Service的價值和意義就體現出來了,你可以創建並啓動一個 Service ,在 Service 裏面就可以一直創建,運行並控制該 Thread了,不用擔心Service退出之後無法再控制Thread,因爲Service會一直運行在後臺,它沒有Activity的界面,不會“顯式”的退出。這樣便解決了上面使用Thread存在的問題。
由以上說明,你應該清楚了他們的各自職責,以及使用場景了。

4、注意事項

在使用Service時也有很多坑需要我們留意。
(1)、在調用 bindService 綁定到Service的時候,應當保證退出時調用 unbindService 解除綁定,這是一個好的習慣,有始有終。儘管 Activity 被 finish 的時候綁定會自動解除,並且Service會自動停止;
(2)、應當注意使用 startService 啓動服務之後,一定要使用 stopService停止服務,不管你是否使用bindService;
(3)、同時使用 startService 與 bindService 要注意到,Service 的終止,需要unbindService與stopService同時調用,才能終止 Service,不管 startService 與 bindService 的調用順序,如果先調用 unbindService 此時服務不會自動終止,再調用 stopService 之後服務纔會停止,如果先調用 stopService 此時服務也不會終止,而再調用 unbindService 或者之前調用 bindService 的 Context 不存在(如Activity 被 finish 的時候)後服務纔會自動停止;
(4)、在 sdk 2.0 及其以後的版本中,對應的 onStart 已經被升級成了 onStartCommand,不過之前的 onStart 任然有效。這意味着,如果你開發的應用程序用的 sdk 爲 2.0 及其以後的版本,那麼你應當使用 onStartCommand 而不是onStart。
(5)、你也可以在 Service 裏註冊 BroadcastReceiver,在其他地方通過發送 broadcast 來控制它,這些是 Thread 做不到的。

5、總結

由以上可見Service和Thread在各自領域都有不同的定義和生命週期,它們基本沒有什麼關係,但是在解決Android的一些問題時需要它們兩個同時上陣,互相搭配才能解決。希望我們認清它們的使用方法,別再被混淆了。

這裏寫圖片描述
本公衆號將以推送Android各種技術乾貨或碎片化知識,以及整理老司機日常工作中踩過的坑涉及到的經驗知識爲主,也會不定期將正在學習使用的新技術總結出來進行分享。每天一點乾貨小知識把你的碎片時間充分利用起來。

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