<1>什麼是服務?
服務是四大元件之一,用于執行長期運作的任務,并且與使用者沒有互動
通俗來說就是長期于背景運作的程式(例如背景播放音樂,背景下載下傳檔案等)
<2>程序的概念
——android系統中程序分為以下幾種:
1,前台程序:顯示在最頂部的,直接跟使用者進行互動的,例如目前操作的activity界面
2,可見程序:可以看見,但不能操作,例如在一個actiivity的頂部彈出一個dialog,此時activity不能點選,但能被看見,就是可見程序
3,服務程序:在背景運作的程序
4,背景程序:隐退到背景,不做事的程序
5,空程序:沒有任何東西在上面跑,僅作為緩存作用, 目的是下次再啟動這個程序的時候會快一點
在手機上,如果按home鍵,目前的activity會退到背景,要麼成為背景程序,要麼成為服務程序
如果按傳回鍵,才會真正退出程式,成為空程序
因為隻有按傳回鍵activity才是調用了ondestory()方法真正銷毀
——如果手機此時記憶體不夠用了,會先殺死哪種程序?
首先殺死空程序,然後殺死背景程序,然後殺死服務,但服務被殺死後等待記憶體夠用時,服務又會重新運作
——service中是否可以執行耗時操作?
不可以
雖然服務是在背景運作的,但是service和activity都是運作在目前app所在的main thread(ui主線程)中的,而耗時操作(如網絡請求、拷貝資料、大檔案)會阻塞主線程,給使用者帶來不好的體驗
如果服務直接執行耗時操作,會出現anr,anr的意思是 android no response(無響應,操作逾時)
在android系統中 前台廣播的anr為10s,背景廣播為60s
activity的操作為5s 前台服務為20s,背景服務為200s
如果需要在服務中進行耗時操作,可以選擇 intentservice,intentservice是service的子類,用來處理異步請求
intentservice在oncreate()方法中通過handlerthread單獨開啟一個線程來處理intent請求對象所對應的任務,這樣可以避免事務處理阻塞主線程
onhandleintent()函數針對intent的不同進行不同的事務處理就可以,執行完一個intent請求對象所對應的工作之後,如果沒有新的intent請求達到,則自動停止service;
否則servicehandler會取得下一個intent請求傳入該函數來處理其所對應的任務
<3>服務的生命周期
因為服務一直都是不可見的,而且使用者無法進行互動操作,是以它的生命周期階段比較少
1,oncreat()
2,onstartcommand()(也可以用onstart(),但是onstart()已經過時了)
3,ondestroy()
服務的啟動和activity一樣,也有顯式意圖建立和隐式意圖建立
<4>第一種開啟服務的方式——startservice
首先建立一個類firstservice繼承自service
在acitivity界面通過顯式意圖(或隐式意圖)的方式來啟動服務和關閉服務
<5>第二種開啟服務的方式——onbind綁定服務
假如我們想使用服務中的一個sayhello()方法,希望在服務運作過程中可以輸出一個hello
首先,在firstservice類中寫這個方法
然後,在mainactivity中new一個firstservice對象去調用這個方法
這樣可行嗎?
答案是不可以,這樣會提示如果要new firstservice調用sayhello方法,那麼這個方法必須是static
而如果把這個sayhello方法改為static,在firstservice類中又會提示擷取不到這個context
為什麼呢?因為四大元件其實都是由系統進行建立的,上下文context都是系統給它配置設定的,如果我們想直接new一個service是不可以的
如果我們想使用服務中的方法,那麼就需要用另一種開啟服務的方法——onbind
我們可以看到,onbind方法的傳回值是一個ibinder對象,是以我們要建立一個對象繼承ibinder作為onbind的傳回值
在這個傳回的innerbinder類的對象中我們就可以調用sayhello()方法了
在mainactivity中要做什麼呢?
首先是綁定服務
綁定的時候bindservice中第二個參數其實是缺少的,我們要建立一個serviceconnection對象
<code>第一個是參數是意圖對象,第二個參數是回調,第三個參數是标記,這個是自動建立的意思,如果服務沒有start,那麼會自己建立</code>
然後是解綁服務
最後,我們要使用服務中的sayhello方法,如何使用呢?
直接用綁定服務時建立的那個mremotebinder對象調用它裡面的callserviceinnermethod方法就好了
因為這個mremotebinder其實就是傳過來的innerbinder
<6>兩種啟動方式的異同
bindservice的特點:
如果onbind()方法傳回的值為null,那麼onserviceconnected方法不會被調用
綁定服務和它所綁的acitivity是不求同生,但求同死的關系,如果activity沒了,那服務也要解綁
服務在解除綁定後會停止運作
綁定的服務隻能解綁一次,多次解綁會抛出異常
綁定的connection要和解鎖的connection對應,否則無法解綁
1,bindservice在系統設定裡的app界面下的running中是沒有顯示的;而startservice是有顯示的
2,startservice來啟動服務,服務是長期在背景運作的,隻有stopservice才會停止服務
就算你的activity都沒有了,這個service也還是在運作;
而bindservice來啟動服務,随着activity的結束,它也會結束
是以在不用的時候要unbindservice,否則會導緻context洩露;
3,starservice不可以通訊(不能調用其中的方法);
而bindservice可以和服務進行通訊
<7>混合啟動服務
由于兩種啟動服務的方式各有優缺點,startservice啟動的服務可以長期背景運作,但不能跟服務進行通訊;bindservice啟動的服務不能長期背景運作,但可以和服務進行通訊
是以我們把兩種方法結合起來,建立一種既可以長期背景運作,又可以進行通訊的服務
startservice的服務的生命周期:oncreate->onstartcommand->ondestroy
bindservice的服務的生命周期:oncreate->onbind->onunbind->ondestroy
結合起來,startservice()->bindservice()->調用服務中的方法->unbindservice()->stopservice()
這樣就可以讓服務長期在背景運作,在解綁服務的時候,并沒有執行ondestroy方法,是以服務還是在運作着的
如果startservice()->bindservice()->stopservice()這樣是無法進行停止服務的
沒有解綁,就沒有辦法執行stopservice()方法
是以在實際開發中的流程是:
1,startservice開啟服務,為了使服務可以長期背景運作
2,bindservice拿到服務的控制binder,可以和服務進行通訊
3,調用服務裡的方法
4,unbindservice解綁服務,否則無法停止服務
(此時解綁了服務,但服務仍然在背景運作)
5,在不需要服務的時候停止服務stopservice