您好,登錄后才能下訂單哦!
Service是一個android的四大組件之一,它沒有UI界面,可以在后臺執行長時間的操作。其他的組件可以start一個Service,service啟動以后會在后臺持續運行,不管用戶是否切換到了其他的應用程序。此外,其他的組件還可以綁定到service上,并和service做交互,甚至還可以執行跨進程的操作(IPC)。比如說,Service可以在后臺處理網絡請求、播放音樂,執行文件i/o或者跟content provider交互。
有兩種形式的service:
Started
當其他的組件通過調用startService()啟動的service是“Started”。一旦啟動以后,service就在后臺無限的運行下去,就算是啟動他的組件已經被銷毀了。通常來說,Started service只會做單一的操作,并且不會返回給調用者返回值。比如:它可以通過網絡下載或者是上傳文件。當操作結束的時候,service要手動把自己停止掉。
Bound
當其他組件通過調用bindService()綁定到service的時候,service就是 "bound"的。bound service提供了客戶端/服務端的接口,允許組件和service進行交互,發送請求,獲取結果,甚至是做跨進程的操作。bound service只存活于其他組件綁定在它上面的時候,多個組件可以同時綁定到一個service,但是,當所有的組件同時解綁以后,service就被銷毀了。
盡管本文檔是分開討論這兩種類型的service的,但是你的service可以同時支持這兩種類型,service可以是started(無限運行)同時還允許綁定。這僅僅取決于你是否實現了下面的回調:onStartCommand()允許其他的組件start一個service,onBind()允許綁定service。
不管你的service是started還是bound,應用的其他組件都可以使用這個service(甚至是別的應用)。就如同別的組件都可以使用一個Activity一樣,通過Intent來啟動它。但是,你也可以在manifest文件中把service聲明成應用私有的,這就阻止了別的應用訪問它。
更多信息請參考“在manifest文件中聲明service”(http://developer.android.com/gui ... ices.html#Declaring)這一節。
注意:service是運行在宿主進程的主線程的,service并不會創建它自己的線程,并不會運行在單獨的進程中,除非你指定了。這意味著,如果你的service要做一些耗CPU或者是阻塞的操作(比如:播放MP3或者是網絡請求),你需要在service內部手動創建一個新的線程來做這樣的操作。通過使用單獨的線程,會降低ANR的風險,讓主線程可以專注于UI操作。
service基礎
要創建一個service,必須要創建Service(或者是Service子類)的子類。在你的實現中,你要重寫一些回調方法,它們用來處理service的一些關鍵的生命周期,并且提供組件綁定到service的機制,如果合適的話。
要重寫的最重要的回調是:
onStartCommand()
當別的組件通過調用startService()來啟動service的時候,系統會調用這個方法。一旦這個方法開始執行,service就啟動起來并在后臺無限運行。如果你覆蓋了這個方法,你還要負責在service的工作結束以后通過調用stopSelf()或者stopService()銷毀掉service。(如果你只是想提供綁定就不需要實現這個方法)
onBind()
當其他的組件通過調用bindService()綁定到service(比如做RPC)的時候,系統會調用這個方法。實現這個方法的時候,必須要通過返回一個IBinder來給客戶端提供一個用來和service進行交互的接口。一般都要實現這個方法,但是如果你不允許綁定的話,可以返回null。
onCreate()
當service首次啟動的時候,系統會調用這個方法,只會執行一次(在調用onStartCommand()或者onBind()之前)。如果service已經在運行了,這個方法是不會被調用的。
onDestroy()
當service不再被使用或者是被銷毀的時候系統會調用這個方法。你的service要實現這個方法來做一些清理資源的工作,比如:線程啊,監聽啊,廣播接收器啊等等。這是service收到的最后一個回調。
如果一個組件通過startService()啟動一個service(會導致系統調用onStartCommand()),service會一直運行,一直到它調用stopSelf()或者是別的組件調用stopService()來把它停止掉。
如果一個組件通過bindService()啟動一個service(onStartCommand()不會被調用),service只存活于綁定到它的組件上。一旦service跟所有的客戶端解綁,它就會被系統結束掉。
Android系統只會在系統的內存不夠用的時候才會強制殺掉service,系統必須能夠給擁有用戶焦點的activity提供系統資源。
如果service被綁定到了擁有用戶焦點的activity上,它很可能不會被殺死。
如果service聲明成了前臺運行的,一般不會被殺死。
其他情況下,如果service是被started并且是長時間運行的,系統會隨著時間的推移把它放到后臺任務列表的靠后的位置,它就會變得易于被殺掉。
如果你的service是被started,你必須要讓它能優雅的處理系統的restart。
如果系統殺掉了你的service,一旦資源可用,系統會立馬restart被殺掉的service(當然這也取決于onStartCommand()的返回值)。
想了解更多關于系統可能會殺掉service的信息,參考:進程和線程(http://developer.android.com/gui ... es-and-threads.html)。
下面的章節,你會學到如何創建各種類型的service,還有如何在其他組件中使用service。
在manifest文件中聲明service
跟Activity類似(還有其他組件),你必須要在manifest文件中聲明所有的service。
要聲明一個service,添加<service>元素作為<application>的子元素,比如:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
想了解更多關于在manifest聲明service的信息,參考<service>元素大全(http://developer.android.com/gui ... ervice-element.html)。
<service>元素還有很多其他的屬性,可以用來定義啟動service的權限或者是service運行的進程。android:name屬性是唯一必須的,它指定了service的類名。一旦你發布了應用以后,就不應該再修改這個名字了。假如你修改了,就會讓用明確Intent來start或者是bind service的代碼有crash的風險。
為了確保你的應用是安全的,當start或者是bind一個service的時候總是使用明確的intent,并且不要給service聲明intent filter。如果允許不明確的方式來啟動service非常重要,你可以給service提供intent filter,排除掉某些組件名字。但是,你必須要用setPackage()給intent設置pachage,這給要調用的目標service提供了足夠的確定性。
此外,你也可以用過引入android:exported這個屬性,并把它的值設置為false來確保你的service只是對你的應用可用。這就有效地防止了其他的應用啟動你的service,就算是使用明確intent也沒有用。
創建一個Started Service
如果別的組件通過調用startService()啟動service的話,service就是一個started service,這會導致系統調用servicve的onStartCommand()方法。
當service啟動以后,它的生命周期跟啟動它的組件的生命周期是相互獨立的,service可以在后臺無限的運行,就算啟動它的組件已經銷毀了。這種情況下,當它的任務完成以后,service就需要調用stopSelf()來停掉自己,其他的組件可以調用stopService()來停掉service。
應用程序的組件比如Activity可以通過調用 startService()來啟動一個service,并且給service傳遞一個Intent,Intent就指定了要啟動的service和傳遞給service使用的數據。service會在onStartCommand()方法中收到這個Intent。
舉個例子,假如一個Activity需要把數據保存到網絡的數據庫上,Activity可以啟動一個service,通過intent傳遞給service要保存的數據。service在onStartCommand()方法中收到intent,連上網絡,做數據庫操作。當操作完成以后,service要停掉自己,然后servive就被銷毀了。
注意:service是運行在應用的同一個進程中,默認是運行在應用的主線程的。因此,如果當用戶跟應用進行交互的時候,service做一些耗時或者是阻塞的操作的話,service會拖慢Activity的性能。為了避免影響應用的性能,你要在service內部新開一個線程。
一般來說,可以通過繼承兩個類來創建started service:
一個是繼承Service
Service是所有service的基類。當你繼承這個類的時候,要在service內部新開一個線程做繁重的操作,因為service默認是運行在應用的主線程的,它會影響應用的性能。
還一個是繼承IntentService
它是Service的子類,它內部使用了一個工作線程來處理所有的請求,一次一個。如果你的service不需要同步與處理多個請求的話,這將是最佳的選擇。你所要做的僅僅是實現onHandleIntent()方法,它會接收請求的intent,因此你就可以做后臺任務。
下面的章節講述了如何用這兩個類實現service。
繼承IntentService類
因為大多數的started service都不需要處理并發的請求,因此使用IntentService可能是最優的方案。
IntentService會做如下的事情:
(1)創建一個默認的工作線程,它用來執行傳遞到onStartCommand()的intent,并且是跟應用的主線程獨立的。
(2)創建一個工作隊列。每次只傳遞一個intent到onHandleIntent()方法,因此就不需要擔心多線程的問題了。
(3)當所有的請求都處理完以后,service會自動停止掉,因此不需要調用stopSelf()。
(4)提供了onBind()的默認實現,方法會返回null。
(5)提供了 onStartCommand()的默認實現,會把intent發送到工作隊列,然后發送給onHandleIntent()。
所有這些以后,你所需要做的僅僅是實現onHandleIntent()來完成客戶的工作(當然你也可以給service提供構造函數)。
下面是一個實現了IntentService的例子:
public class HelloIntentService extends IntentService { /** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. */ public HelloIntentService() { super("HelloIntentService"); } /** * The IntentService calls this method from the default worker thread with * the intent that started the service. When this method returns, IntentService * stops the service, as appropriate. */ @Override protected void onHandleIntent(Intent intent) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } }}
這就是你要做的全部的事情:一個構造函數和實現onHandleIntent()。
如果你想覆蓋別的回調比如onCreate(), onStartCommand(),或者onDestroy(),一定要記得調用父類的實現,這樣IntentService才可以正確的處理工作線程的生命周期。
比如下面,onStartCommand()必須要返回默認的實現(跟intent被傳遞到onHandleIntent()一樣)。
@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent,flags,startId);}
除了onHandleIntent(),唯一個不需要調用父類的函數是onBind()(只有不允許綁定的時候才需要實現這個方法)。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。