文章轉自:https://cloud.tencent.com/developer/article/1341705
Services
??服務是一個應用程序組件,可以在后臺執(zhí)行長時間運行的操作,不提供用戶界面貌矿。一個應用程序組件可以啟動一個服務,它將繼續(xù)在后臺運行,即使用戶切換到另一個應用程序。此外,一個組件可以綁定到一個服務與它交互,甚至執(zhí)行進程間通信(IPC)。例如,一個服務可能處理網(wǎng)絡通信,播放音樂,執(zhí)行文件I/O,或與一個內容提供者交互,都在后臺執(zhí)行榛做。
一個服務本質上講有兩種形式:
Started 啟動的
started形式的服務是指當一個應用組件(比如activity)通過startService()方法開啟的服務。一旦開啟,該服務就可以無限期地在后臺運行,哪怕開啟它的組件被銷毀掉咆霜。通常,開啟的服務執(zhí)行一個單獨的操作且并不向調用者返回一個結果。比如卵迂,可能從網(wǎng)絡進行下載或者上傳一個文件裕便。當任務完成,服務就該自我停止见咒。
Bound 綁定的
bound形式的服務是指一個應用組件通過調用 bindService() 方法與服務綁定偿衰。一個綁定的服務提供一個客戶-服務端接口,以允許組件與服務交互改览,發(fā)送請求下翎,獲得結果,甚至執(zhí)行進程間通信宝当。一個綁定的服務只和與其綁定的組件同時運行视事。多個組件可以同時綁定到一個服務,但當全部接觸綁定后庆揩,服務就被銷毀俐东。
雖然分這兩類,但是一個服務可以同時使用這兩種方式——可以用started無限期的運行订晌,同時允許綁定虏辫。只需要在服務中實現(xiàn)兩個回調方法:onStartCommand()允許組件開啟服務,onBind()允許綁定锈拨。
不論應用程序是怎么起服務的砌庄,任何應用程序都可以用這個服務。同樣的奕枢,任何組件可以使用一個Activity通過傳遞Intent開啟服務娄昆。你也可以在配置文件設置服務為私有來防止其他應用訪問該服務。
注意:一個服務在進程中的主線程運行——一個服務不會創(chuàng)建自己的線程缝彬,也不會在另外的進程運行(除非另外指定)萌焰。這意味著,如果服務需要做一些頻繁占用CPU的工作或者會發(fā)生阻塞的操作谷浅,你需要在服務中另開線程執(zhí)行任務扒俯。這可以降低產(chǎn)生ANR的風險族购,提高用戶體驗。
基礎
創(chuàng)建一個服務需要建立一個Service相關的子類陵珍,然后需要實現(xiàn)一些回調方法,好在不同的生命周期內做對應處理和綁定服務违施,比較重要的方法如下:
onStartCommand()當其他組件互纯,如 activity 請求服務啟動時,系統(tǒng)會調用這個方法磕蒲。一旦這個方法執(zhí)行留潦,服務就開始并且無限期的執(zhí)行。如果實現(xiàn)這個方法辣往,當這個服務完成任務后兔院,需要你來調用 stopSelf() 或者 stopService() 停掉服務。如果只想提供綁定站削,不需要自己實現(xiàn)這個方法坊萝。
onBind()當有其他組件想通過 bindService() 方法綁定這個服務時系統(tǒng)就會調用此方法。在實現(xiàn)的方法里面许起,必須添加一個供客戶端使用的接口通過返回一個IBinder來與服務通信十偶,這個方法必須實現(xiàn)。當然不想允許綁定的話园细,返回null即可惦积。
onCreate()服務第一次建立的時候會調用這個方法,執(zhí)行一次性設置程序猛频,在上面2個方法執(zhí)行前調用狮崩。如果服務已存在,則不執(zhí)行該方法鹿寻。
onDestroy()服務不再使用則使用該方法睦柴。服務應該實現(xiàn)這個方法來清理諸如線程,注冊的監(jiān)聽器等資源烈和。這是最后調用的方法爱只。
安卓系統(tǒng)只會在內存占用很高,必須恢復系統(tǒng)資源供當前運行程序的情況下強制停掉一個運行中的服務招刹。如果服務綁定在當前運行的程序中恬试,就幾乎不會被殺掉,如果服務聲明了在前臺運行(其實在后臺疯暑,只是給系統(tǒng)一個錯的信息來提高優(yōu)先級)训柴,就幾乎不會被殺掉。另外妇拯,如果一個服務正在運行幻馁,且運行了很久洗鸵,系統(tǒng)就會根據(jù)運行時間把其排在后臺任務列表的后面,則這個服務很容易被殺掉仗嗦。根據(jù)onStartCommand()的返回值設置膘滨,服務被殺掉后仍可以在資源充足的條件下立即重啟。
是用一個服務好還是開一個線程好一個服務就是一個可以忽略交互稀拐,在后臺獨立運行的組件火邓,如果你需要這樣就用服務如果你需要在用戶與程序交互時在主線程外執(zhí)行任務,那就開個線程吧德撬。比如想播放音樂铲咨,但只在程序運行時播放,你可能在 onCreate() 開一個線程蜓洪,在 onStart() 中開啟它纤勒,在 onStop() 停止它。也可以考慮使用AsyncTask或者HandlerThread取代一般的線程隆檀。記住摇天,如果使用一個服務,它還是默認在主線程中運行恐仑,如果會發(fā)生阻塞闸翅,還是要在服務中另開線程的脉幢。
在 manifest 文件聲明服務
要使用服務就必須在manifest文件聲明要用的所有服務等浊,只用在<application>標簽內添加子標簽<service>即可。
下面對service標簽屬性做說明
android:name? 你所編寫的服務類的類名导狡,可填寫完整名稱鉴逞,包名+類名记某,如com.example.test.ServiceA,也可以忽略包名构捡,用.開頭液南,如.ServiceA,因為在manifest文件開頭會定義包名勾徽,它會自己引用滑凉。
一旦你發(fā)布應用,你就不能改這個名字(除非設置android:exported="false")喘帚,另外name沒有默認值畅姊,必須定義。
android:enabled? 是否可以被系統(tǒng)實例化吹由,默認為true
android:exported? 其他應用能否訪問該服務若未,如果不能,則只有本應用或有相同用戶ID的應用能訪問倾鲫。當然除了該屬性也可以在下面permission中限制其他應用訪問本服務粗合。? 這個默認值與服務是否包含意圖過濾器intent filters有關萍嬉。如果一個也沒有則為false
android:isolatedProcess? 設置true意味著,服務會在一個特殊的進程下運行隙疚,這個進程與系統(tǒng)其他進程分開且沒有自己的權限壤追。與其通信的唯一途徑是通過服務的API(binding and starting)。
android:label? 可以顯示給用戶的服務名稱供屉。如果沒設置大诸,就用<application>的lable。不管怎樣贯卦,這個值是所有服務的意圖過濾器的默認lable。定義盡量用對字符串資源的引用焙贷。
android:icon? 類似label撵割,是圖標,盡量用drawable資源的引用定義辙芍。
android:permission? 是一個實體必須要運行或綁定一個服務的權限啡彬。如果沒有權限,startService()故硅,bindService()或stopService()方法將不執(zhí)行庶灿,Intent也不會傳遞到服務。? 如果屬性未設置吃衅,會由<application>權限設置情況應用到服務往踢。如果兩者都未設置,服務就不受權限保護徘层。
android:process? 服務運行所在的進程名峻呕。通常為默認為應用程序所在的進程,與包名同名趣效。<application>元素的屬性process可以設置不同的進程名瘦癌,當然組件也可設置自己的進程覆蓋應用的設置。? 如果名稱設置為冒號:開頭跷敬,一個對應用程序私有的新進程會在需要時和運行到這個進程時建立讯私。如果名稱為小寫字母開頭,服務會在一個相同名字的全局進程運行西傀,如果有權限這樣的話斤寇。這允許不同應用程序的組件可以分享一個進程,減少了資源的使用拥褂。
創(chuàng)建“啟動的”服務
啟動的(started)服務由startService(Intent)方法啟動抡驼,在服務中的onStartCommand()方法里獲得Intent信息。關閉則由服務自己的方法stopSelf()或者由啟動服務的地方調用stopService(Intent)方法來關閉肿仑。并不會因為啟動服務的應用程序銷毀而關閉致盟。
??示例碎税,一個應用需要保存數(shù)據(jù)到遠程數(shù)據(jù)庫,這時啟動一個服務馏锡,通過創(chuàng)建啟動的服務給服務傳遞數(shù)據(jù)雷蹂,由服務執(zhí)行保存行為,行為結束再自我銷毀杯道。因為服務跟啟動它的應用在一個進程的主線程中匪煌,所以對于耗時的操作要起一個新的線程去做。
寫服務有2種党巾,繼承service或者IntentService萎庭。后者是前者的子類。前者包含上面介紹的各種方法齿拂,用于普通的服務驳规。后者可以自己開一個工作線程一個接一個處理多個請求。
繼承IntentService
大多數(shù)服務不需要同時處理多個請求署海,繼承IntentService是最好的選擇
IntentService處理流程
創(chuàng)建默認的一個worker線程處理傳遞給onStartCommand()的所有intent吗购,不占據(jù)應用的主線程
創(chuàng)建一個工作隊列一次傳遞一個intent到你實現(xiàn)的onHandleIntent()方法,避免了多線程
在所以啟動請求被處理后自動關閉服務砸狞,不需要調用stopSelf()
默認提供onBind()的實現(xiàn)捻勉,并返回null
默認提供onStartCommand()的實現(xiàn),實現(xiàn)發(fā)送intent到工作隊列再到你的onHandleIntent()方法實現(xiàn)刀森。
這些都加入到IntentService中了踱启,你需要做的就是實現(xiàn)構造方法和onHandleIntent(),如下:
如果需要重寫其他回調方法研底,如onCreate(),onStartCommand()等禽捆,一定要調用super()方法,保證IntentService正確處理worker線程飘哨,只有onHandleIntent()和onBind()不需要這樣胚想。如:
繼承Service
繼承Service就可以實現(xiàn)對請求多線程的處理,前面介紹了service的生命周期芽隆,可以按照生命周期實現(xiàn)方法浊服。就不放示例了。
onStartCommand()的返回值返回一個整型值胚吁,用來描述系統(tǒng)在殺掉服務后是否要繼續(xù)啟動服務牙躺,返回值有三種:
START_NOT_STICKY? 系統(tǒng)不重新創(chuàng)建服務,除非有將要傳遞來的intent腕扶。這是最安全的選項孽拷,可以避免在不必要的時候運行服務。
START_STICKY? 系統(tǒng)重新創(chuàng)建服務并且調用onStartCommand()方法半抱,但并不會傳遞最后一次傳遞的intent脓恕,只是傳遞一個空的intent膜宋。除非存在將要傳遞來的intent,那么就會傳遞這些intent炼幔。這個適合播放器一類的服務秋茫,不需要執(zhí)行命令,只需要獨自運行乃秀,等待任務肛著。
START_REDELIVER_INTENT? 系統(tǒng)重新創(chuàng)建服務并且調用onStartCommand()方法,傳遞最后一次傳遞的intent跺讯。其余存在的需要傳遞的intent會按順序傳遞進來枢贿。這適合像下載一樣的服務,立即恢復刀脏,積極執(zhí)行局荚。
如果想從服務獲得結果,可以用廣播來處理
創(chuàng)建“綁定的”服務
用bindService()方法將應用組件綁定到服務火本,建立一個長時間保持的聯(lián)系。
如果需要在activity或其他組件和服務交互或者通過進程間通信給其他應用程序提供本應用的功能聪建,就需要綁定的服務钙畔。
建立一個綁定的服務需要實現(xiàn)onBind()方法返回一個定義了與服務通信接口的IBinder對象。其他應用程序組件可以調用bindService()方法獲取接口并且調用服務上的方法金麸。
創(chuàng)建一個綁定的服務擎析,第一件事就是定義一個說明客戶端與服務通信方式的接口。這個接口必須是IBinder的實現(xiàn)挥下,并且必須要從onBind()方法返回揍魂。一旦客戶端接收到了IBinder,就可以通過這個接口進行交互棚瘟。
多個客戶端可以綁定到一個服務现斋,可以用unbindService()方法解除綁定,當沒有組件綁定在服務上偎蘸,這個服務就會被銷毀庄蹋。
啟動前臺服務
前臺服務是被認為是用戶已知的正在運行的服務,當系統(tǒng)需要釋放內存時不會優(yōu)先殺掉該進程迷雪。前臺進程必須發(fā)一個notification在狀態(tài)欄中顯示限书,直到進程被殺死。因為前臺服務會一直消耗一部分資源章咧,但不像一般服務那樣會在需要的時候被殺掉倦西,所以為了能節(jié)約資源,保護電池壽命赁严,一定要在建前臺服務的時候發(fā)notification扰柠,提示用戶粉铐。當然,系統(tǒng)提供的方法就是必須有notification參數(shù)的耻矮,所以不要想著怎么把notification隱藏掉秦躯。
startForeground()方法就是將服務設為前臺服務。參數(shù)12346就是這個通知唯一的id裆装,只要不為0即可踱承。
服務的生命周期
啟動的服務:
? startService()->onCreate()->onStartCommand()->running->stopService()/stopSelf()->onDestroy()->stopped
? 其中,服務未運行時會調用一次onCreate()哨免,運行時不調用茎活。
綁定的服務:
? bindService()->onCreate()->onBind()->running->onUnbind()->onDestroy()->stopped
服務起始于onCreate(),終止于onDestory()服務的開關過程琢唾,只有onStartCommand()可多次調用载荔,其他在一個生命周期只調用一次。
這兩個過程并不完全獨立采桃,也可以綁定一個由startService()啟動過的服務
關于怎樣讓服務不被殺死
??這個倒是有點流氓軟件的意思懒熙,但有些特定情況還是需要服務能保持開啟不被殺死,當然這樣做我還是在程序里添加了關閉服務的按鈕普办,也就是開啟了就殺不死工扎,除非在軟件里關閉。
服務不被殺死分3種來討論1.系統(tǒng)根據(jù)資源分配情況殺死服務2.用戶通過settings->Apps->Running->Stop方式殺死服務3.用戶通過settings->Apps->Downloaded->Force Stop方式殺死服務
第一種情況:用戶不干預衔蹲,完全靠系統(tǒng)來控制肢娘,辦法有很多。比如onStartCommand()方法的返回值設為START_STICKY舆驶,服務就會在資源緊張的時候被殺掉橱健,然后在資源足夠的時候再恢復。當然也可設置為前臺服務沙廉,使其有高的優(yōu)先級拘荡,在資源緊張的時候也不會被殺掉。
第二種情況:用戶干預撬陵,主動殺掉運行中的服務俱病。這個過程殺死服務會通過服務的生命周期,也就是會調用onDestory()方法袱结,這時候一個方案就是在onDestory()中發(fā)送廣播開啟自己亮隙。這樣殺死服務后會立即啟動。如下:
當然垢夹,從理論上來講這個方案是可行的溢吻,實驗一下也可以。但有些情況下,發(fā)送的廣播在消息隊列中排的靠后促王,就有可能服務還沒接收到廣播就銷毀了(這是我對實驗結果的猜想犀盟,具體執(zhí)行步驟暫時還不了解)。所以為了能讓這個機制完美運行蝇狼,可以開啟兩個服務阅畴,相互監(jiān)聽,相互啟動迅耘。服務A監(jiān)聽B的廣播來啟動B贱枣,服務B監(jiān)聽A的廣播來啟動A。經(jīng)過實驗颤专,這個方案可行纽哥,并且用360殺掉后幾秒后服務也還是能自啟的。到這里再說一句栖秕,如果不是某些功能需要的服務春塌,不建議這么做,會降低用戶體驗簇捍。
第三種情況:強制關閉就沒有辦法只壳。這個好像是從包的level去關的,并不走完整的生命周期暑塑。所以在服務里加代碼是無法被調用的吼句。處理這個情況的唯一方法是屏蔽掉force stop和uninstall按鈕,讓其不可用梯投。方法自己去找吧命辖。當然有些手機自帶的清理功能就是從這個地方清理的况毅,比如華為的清理分蓖。所以第三種情況我也沒有什么更好的辦法了。
最后再說一句尔许,別在這上面太折騰么鹤,弄成流氓軟件就不好了。我就是討厭一些軟件亂發(fā)通知味廊,起服務才轉而用iPhone的蒸甜。不過下一代Android好像可以支持用戶選擇是否開啟軟件設置的權限了,倒是可以期待一下余佛。推薦一篇文章:Diamonds Are Forever. Services Are Not.
補充:檢測服務是否在運行的方法柠新,就是獲取所有正在運行的服務,一一與相應的服務名稱做比較: