Service是一個android的四大組件之一,它沒有UI界面,可以在後臺執行長時間的操作。其他的組件可以start一個Service,service啓動以後會在後臺持續運行,不管用戶是否切換到了其他的應用程序。此外,其他的組件還可以綁定到service上,並和service做交互,甚至還可以執行跨進程的操作(IPC)。比如說,Service可以在後臺處理網絡請求、播放音樂,執行文件i/o或者跟content provider交互。 Bound 更多信息請參考“在manifest文件中聲明service”(http://developer.android.com/gui ... ices.html#Declaring)這一節。 要創建一個service,必須要創建Service(或者是Service子類)的子類。在你的實現中,你要重寫一些回調方法,它們用來處理service的一些關鍵的生命週期,並且提供組件綁定到service的機制,如果合適的話。 要重寫的最重要的回調是: 當別的組件通過調用startService()來啓動service的時候,系統會調用這個方法。一旦這個方法開始執行,service就啓動起來並在後臺無限運行。如果你覆蓋了這個方法,你還要負責在service的工作結束以後通過調用stopSelf()或者stopService()銷燬掉service。(如果你只是想提供綁定就不需要實現這個方法) 這就是你要做的全部的事情:一個構造函數和實現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()(只有不允許綁定的時候才需要實現這個方法)。 下一節中,你會看到如何繼承不同的基類來實現同樣的功能,代碼有點多,但是如果你要處理併發的請求的話可能會很有用。 繼承Service類 正如在上節中看到的那樣,使用IntentService讓實現一個started service變得很簡單。但是,如果你的service需要處理多線程(不是通過工作隊列來處理請求),那麼你可以繼承Service類來處理intent。 作爲一個對比,下面的例子是一個實現了Service類來完成上面使用IntentService同樣的功能,也就是,給每一個請求,都使用工作線程來完成工作,一次只處理一個請求。 public class HelloService extends Service { private Looper mServiceLooper; private ServiceHandler mServiceHandler; // Handler that receives messages from the thread private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // 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) { } } } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1); } } @Override public void onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work will not disrupt our UI. HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // We don't provide binding, so return null return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); }} 可以看出來,與使用IntentService相比要做很多額外的工作。 但是,因爲你在onStartCommand()中處理請求,所以你可以併發處理多個請求。這個就超出這個例子的範圍了。但是如果你需要這麼做,你可以給每個請求創建一個線程然後立即執行(而不是等待前一個請求執行結束)。 要注意的是,onStartCommand()必須要返回一個整數返回值。返回值描述了service被系統殺掉以後又被restart的時候,系統如何處理service(正如之前討論的那樣,IntentService默認會幫你做這個事,當然你也可以修改)。返回值必須是下面的幾個常量: START_NOT_STICKY 如果onStartCommand()返回以後系統殺掉了service,restart的時候不會重新創建service,除非有pending intent要發送。這是最安全的方式來避免運行不必要的service,並且應用可以簡單地重新開始任何沒完成的任務。 START_STICKY 如果onStartCommand()返回以後系統殺掉了service,restart的時候會重新創建service,然後調用onStartCommand(),但是不會再次發送最後的intent。相反,系統會用null的intent來調用onStartCommand(),除非是有pending intent來啓動service,這種情況下,這些intent是會傳遞進去的。這對媒體播放器(或者類似的service)來說是非常合適的,因爲它們不執行命令,只是在無限的運行,等待新的任務。 START_REDELIVER_INTENT 如果onStartCommand()返回以後系統殺掉了service,restart的時候會重新創建service,然後調用onStartCommand(),並且會再次發送最後的intent。pending intents會被依次發送,這適合於那種需要立即被喚醒的情況,比如:下載文件。 瞭解更多關於返回值的信息,參考每一個常量的文檔。 啓動Service 你可以在Activity中後者是其他組件中通過給startService()傳遞intent(指定要啓動的service)來啓動service。Android系統會調用service的onStartCommand()方法然後把Intent傳遞進去(禁止直接調用onStartCommand())。 比如,Activity可以在startService()中用明確的intent啓動前面章節中的例子service(HelloService)。 Intent intent = new Intent(this, HelloService.class);startService(intent); startService()方法會立即返回,Android系統會調用service的onStartCommand()方法。如果service還沒有執行過,系統會首先調用onCreate()然後再調用onStartCommand()。 如果service不提供綁定,startService()方法傳遞進來的intent是唯一的跟外部組件進行通訊的方式。但是,如果你想讓service發送結果出去,啓動service的客戶端可以創建一個做廣播用的PendingIntent,(用getBroadcast()),把PendingIntent傳遞給service。service就可以使用廣播來傳遞結果。 多個啓動service的請求會導致相應的對service的多個onStartCommand()調用。但是,只需要一個停止service的請求就可以停掉service。 停掉service started service必須要自己管理生命週期。也就是說,系統不會停止或者銷燬service除非是爲了釋放系統資源,service在onStartCommand()返回以後會繼續運行。因此,service必須要調用stopSelf()停掉自己或者是其他組件調用stopService()來停掉service。 一旦用stopSelf()或者stopService()請求停掉service,系統就會盡快的將service銷燬掉。 但是,如果你的service在onStartCommand()中併發的處理請求,當你處理完一個請求以後你不應該就停掉service,因爲你可能又收到了一個新的請求(第一個請求結束後停掉service也會停掉第二個請求)。爲了避免這樣的事情,你可以使用stopSelf(int)來確保停止service的請求總是基於最近的請求。也就是說,當你調用stopSelf(int)的時候,你要傳遞對應停止請求的啓動請求ID(startId會被傳遞到onStartCommand()中)。然後,如果service在調用stopSelf(int)之前,又收到了一個啓動請求,ID匹配不上,service就不會停止。 注意:在service工作完成的時候把service停掉對於避免浪費系統資源和節省電量是很重要的。如果需要的話,其他的組件可以調用stopService()來停掉service。啓用了binding的service,如果收到了onStartCommand()調用的話,也要手動停掉service。 瞭解更多關於service生命週期的知識,查看“管理Service的生命週期”下面的章節。 創建Bound Service bound service允許應用的組件調用bindService()綁定到service上,爲了創建長時間存在的連接(一般不允許組件調用startService()來啓動)。 當你想從Activity或者是其他組件跟service進行交互,或者是通過IPC暴漏應用的某些功能給其他應用的時候,你應該創建bound service。 要創建一個bound service,必須要實現onBind()回調,並且返回IBinder,它定義了跟service通訊的接口。其他的組件可以調用bindService()來檢索出接口,然後調用service的方法。service只存活於它綁定到的組件上,所以,當沒有組件綁定的時候,系統會銷燬它(你不需要像停掉started service那樣來停掉bound service)。 要創建一個bound service,首先要做的是定義客戶端跟service交互的接口。service和客戶端之間的接口必須是IBinder的實現,並且service必須要在onBind()回調中返回,一旦客戶端收到了IBinder,它就可以通過接口跟service交互。 多個客戶端可以同時綁定到同一個service上,當一個客戶端跟service交互完成以後,它會調用unbindService()來解綁。如果沒有客戶端綁定到service上,系統就會把service銷燬掉。 有很多種實現bound service的方式,一般實現要比started service更復雜,所以bound service會在單獨的文章(http://developer.android.com/guide/components/bound-services.html)中做討論。 給用戶發送Notification 一旦運行以後,service可以使用彈出Notification或者是狀態欄Notification給用戶發送提醒事件。 彈出提醒是出現在當前窗口之上停留一段時間然後消失的消息提示,狀態欄提醒在狀態欄的消息中提供了一個icon,用戶可以選中做一些操作(比如啓動一個Activity)。 一般來說,當後臺任務完成以後,用狀態欄提醒是最佳的方式(比如:文件下載完成),然後用戶可以做一些動作。當用戶在展開的視圖中選擇一個提醒以後,提醒可以開啓一個Activity(比如跳轉到下載文件的view)。 前臺運行service
注意: ID一定不能爲0. 要從前臺移除service,需要調用stopForeground()。這個方法接收一個boolean參數,用以表明是否移除狀態欄的提醒。這個方法不會停掉service,但是,如果你想在service運行在前臺的時候停掉它,提醒也會被移除掉。 管理service的生命週期 service的生命週期要遠比Activity的生命週期簡單。但是,你需要更加註意service的創建和銷燬,因爲service可以在用戶不知曉的情況下在後臺運行。 service生命週期-從創建到銷燬-遵循下面的2中不同的形式: (1)started service 當其他組件調用startService()的時候service被創建出來,然後就無限的運行,它必須要手動調用stopSelf()來停掉自己。其他的組件可以調用stopService()來停掉它。當service停掉以後,系統會銷燬它。
注意:跟Activity的生命週期回調不同的是,這些回調並不要求調用父類的實現。 通過實現這些方法,你可以監控service生命週期內部的2個循環: (1)service的完整的生命週期是從調用onCreate()到onDestroy()返回。跟activity類似,service也是在onCreate()中做初始化,在onDestroy()中釋放資源。比如,音樂播放器service可以在onCreate()中創建播放音樂的線程,在onDestroy()中停掉這個線程。所有的service都會調用onCreate()和onDestroy(),不管service是用startService()還是bindService()創建出來。 (2)service的活動的時間是從調用onStartCommand()或者onBind()開始的.與此對應,這兩個方法會處理通過startService()或者bindService()傳遞進去的intent。如果service是被started,當service的生命週期結束的時候也就是活動時間結束的時候(從onStartCommand()返回以後,service仍然是活動的)。如果service是bound的,onUnbind()返回的時候,service的生命也就結束了。 注意:如果一個started service是通過調用stopSelf()或者stopService()被停掉的,這時候並沒有與之對應的回調(沒有onStop()回調)。 因此,除非service是被綁定到了一個客戶端上,否則一旦service停掉,系統就會去銷燬它,onDestroy()是service收到的唯一的回調。 |
Android-Service組件
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.