Android-Service組件

來源:互聯網
上載者:User

標籤:des   android   blog   http   io   os   ar   使用   java   

轉載請標明出處:http://blog.csdn.net/goldenfish1919/article/details/40381109

原文:http://developer.android.com/guide/components/services.html

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/guide/components/services.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/guide/components/processes-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/guide/topics/manifest/service-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()(只有不允許綁定的時候才需要實現這個方法)。

下一節中,你會看到如何繼承不同的基類來實現同樣的功能,代碼有點多,但是如果你要處理並發的請求的話可能會很有用。

繼承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


前台service是被認為是使用者可感知的,當系統記憶體低的時候不是候選被殺掉的service。前台service必須在狀態列有提醒,放在“進行中”的頭部之下,也就是說,提醒不會消失除非service被停止或者被從前台移除。

比如說,service的音樂播放器應該運行在前台,因為使用者明確感知到它的操作,狀態列的提醒可以表明當前現正播放的歌曲,並且允許使用者跳轉到音樂播放器的Activity做一些互動。

讓service運行在前台需要調用startForeground()方法。這個方法接受2個參數,一個唯一標識提醒的整數,一個是狀態列的提醒。比如:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),System.currentTimeMillis());Intent notificationIntent = new Intent(this, ExampleActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);notification.setLatestEventInfo(this, getText(R.string.notification_title),getText(R.string.notification_message), pendingIntent);startForeground(ONGOING_NOTIFICATION_ID, notification);

注意: ID一定不能為0.

要從前台移除service,需要調用stopForeground()。這個方法接收一個boolean參數,用以表明是否移除狀態列的提醒。這個方法不會停掉service,但是,如果你想在service運行在前台的時候停掉它,提醒也會被移除掉。

管理service的生命週期

service的生命週期要遠比Activity的生命週期簡單。但是,你需要更加註意service的建立和銷毀,因為service可以在使用者不知曉的情況下在後台運行。

service生命週期-從建立到銷毀-遵循下面的2中不同的形式:

(1)started service
當其他組件調用startService()的時候service被建立出來,然後就無限的運行,它必須要手動調用stopSelf()來停掉自己。其他的組件可以調用stopService()來停掉它。當service停掉以後,系統會銷毀它。


(2)bound service
當其他組件(用戶端)調用bindService()時候service被建立出來,然後用戶端通過IBinder介面跟service進行互動。用戶端可以調用unbindService()來關掉與service的串連。多個用戶端可以同時串連到一個service上,當所有的用戶端都解除綁定以後,系統會銷毀掉service(service不需要自己手動去停掉)。

這兩種形式並不是完全獨立的。也就是說,你可以綁定到一個用startService()啟動的started service上。比如:背景音樂service可以通過startService()傳遞要播放的歌曲的intent來啟動起來,然後,當使用者希望對播放器做一些操作或者是擷取當前歌曲的資訊的時候,可以把Activity通過bindService()綁定到service上。這種情況下,stopService()或者stopSelf()並不會停掉service,直到所有的用戶端都解除綁定以後。

實現生命週期回調

類似於Activity,service也有生命收合回呼函數可以讓你來監控service的狀態,並在適當的時候做一些事情。


下面的例子展示了每一個生命週期函數:

public class ExampleService extends Service {    int mStartMode;       // indicates how to behave if the service is killed    IBinder mBinder;      // interface for clients that bind    boolean mAllowRebind; // indicates whether onRebind should be used    @Override    public void onCreate() {        // The service is being created    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        // The service is starting, due to a call to startService()        return mStartMode;    }    @Override    public IBinder onBind(Intent intent) {        // A client is binding to the service with bindService()        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        // All clients have unbound with unbindService()        return mAllowRebind;    }    @Override    public void onRebind(Intent intent) {        // A client is binding to the service with bindService(),        // after onUnbind() has already been called    }    @Override    public void onDestroy() {        // The service is no longer used and is being destroyed    }}

注意:跟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組件

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.