【android官方文檔節選】Service

如轉載請註明出處http://blog.csdn.net/ethanchiu/article/details/19413151

Services

一個服務有兩種形式

1.Started

     一旦一個組件調用了startService(),service就是“started”狀態,一旦開始,即使啓動它的組件銷燬了,一個service可以無限期的運行在後臺。通常一個啓動的service執行一個操作,並且不會返回結果給喚起者。比如,可以通過網絡下載或者上傳一個文件。當操作完成,服務應該停止自己。

2.Bound

     一旦一個組件調用了bindService(),service就是“bound”狀態。一個bound service提供一個clientserver接口,這個接口允許組件和service交互,發送請求,獲得結果,甚至進程間通信。一個bound service只有在和他綁定的時候纔會運行。多個組件可以一次綁定到service,當所有的組件都解綁的時候,service就銷燬了。

     雖然文檔是分開討論者兩種service的,但是serivce可以同時工作在這兩種方式上—即可以啓動,又可以綁定。只要看是否實現了一對回調方法:onStartCommand()和onBind()。

警告:一個service運行在主線程中--service不會創建一個自己的線程,不會運行在另一個進程中(除非指定了)。這就意味了,如果service執行cpu敏感的工作或者阻塞操作(比如MP3播放或者網絡工作),需要在service中創建一個線程去做那些工作。通過一個單獨的線程,可以減少ANR的風險,主線程可以保持只做用戶交互先關的任務。

基礎

    onStartCommand()
     當其他組件比如activity通過調用startService()請求的service開始了,這個方法將會被調用。一旦這個方法執行,service就會啓動,並且一直運行在後臺。如果你實現這個方法,service的任務完成的時候,就有責任停止這個服務,通過調用stopSelf()或者stopService()。(如果只是想提供綁定,就沒必要實現這個方法)。
      onBind()
     當另一個組件通過調用bindService()想綁定這個service的時候,系統調用這個方法。在這個方法的實現中,通過返回一個IBinder提供一個用戶和service交互的接口。如果不想綁定,返回null。
     onCreate()
     service第一次創建的時候,系統調用這個方法,來執行一次安裝進程。如果service已經運行,這個方法不會調用。
     onDestroy()
     當service不再使用銷燬的時候調用。service應該實現這個方法去清除一些資源比如線程,註冊的監聽器,接受器,等等。

     通過startService()啓動一個組件(會調用onStartCommand),它會一直保持運行,直到自己調用stopSelf()或者另一個組件調用stopService()。
     通過調用bindService()創建一個service(onStartCommand不會回調),service只會在組件綁定的時候運行。如果所有組件都解綁了,這個service就會銷燬。

     當內存低的時候,系統會強制停止一個服務,來恢復系統資源給有用戶焦點的Activity。如果一個Activity獲得焦點並且綁定了service,那麼這個service被殺死的可能性比較小,並且如果service被聲明前端運行,那麼這個service幾乎不會被殺死。除此之外,如果service開始了並且長時間運行,那麼系統會降低它的後臺任務優先級並且很可能被系統殺死--如果service啓動了,你就該設計一個讓系統重新啓動它的機制。如果系統殺死了service,一旦資源可用之後應該重啓它。更多關於系統殺死service,看Processes and Threading部分。
     

在manifest聲明

     如果要想只在自己的app中使用這個service,需要加入android:exported屬性,並且設置爲false。這個可以有效停止其他app啓動這個service,甚至使用了隱式intent也不可以。

創建一個started service     

 有兩種類可以實現一個started service

Service
     是所有services的基類。當集成這個類,最重要的是創建一個新的線程去做所有的service任務,因爲這個service使用了程序的主線程。

IntentService
     這是Service的子類,它使用一個工作線程處理所有的開始請求任務。如果不需要service同時處理多個請求,這是最好的選擇。實現 onHandleIntent(),可以接收到每個請求返回的intent。

繼承自IntentService類

     因爲大多數started services 不需要同是處理多個請求(就是危險的多線程方案),IntentService是最好的選擇。
     IntentService的功能:
創建一個默認的線程去處理所有通過onStartCommand()傳過來的intents。
創建一個工作隊列,在 onHandleIntent()中一時間傳過來一個,隊列處理,所以不要擔心多線程。
所有請求處理完成之後,自己停止服務,不需要調用stopSelf()。
提供返回null的onBind() 的默認實現
提供onStartCommand()的默認實現,它發送intent到工作隊列。

     所以只要實現了onHandleIntent()去做需要做的任務就行了。

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) {
              }
          }
      }
  }
}

     如果想重寫onCreate(),onStartCommand(),或者onDestroy(),確保調用了父類實現,這樣IntentService可以在工作線程中處理任務。
     比如,onStartCommand()必須返回默認實現(這就是intent傳遞到onHandleIntent()的原因):
@Override
public 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類

     如果要處理多線程的任務,需要使用Service類去處理每一個intent。
     
     以下是實現和IntentService一樣功能,service對應的代碼。
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();
  }
}
     在onStartCommand()方法中必須返回一個整型數據。這個整型描述了當系統殺掉它的情況下,系統那個將如何繼續這個service。這個返回的整型如下:
     
開始一個Service     
     如果想service發送一個結果返回,啓動service的組件可以爲broadcast(使用getBroadcast())創建一個PedingIntent並且在Intent中將它傳遞給service。service可以使用broadcast傳遞一個結果。

停止一個Service
     在service中停止服務使用stopSelf(),在其他組件中停止服務調用stopService()。
     如果service在onstartcommand處理多個請求,不應該停止服務當處理完一個請求的時候,因爲你可能接收到了一個新的請求(在一個請求完畢之後,停止操作可能終止了第二個請求)。爲了避免這種情況,可以使用stopSelf(int)來確保停止的請求是之前發起的對應請求。

創建一個Bound Service     

一個bound Service是允許組件爲了創建一個長時間連接的的服務。

     
     這個service的存在是服務於和它綁定的組件的,所以當沒有組件綁定它的時候,這個service就是銷燬。
     
     創建一個bound service,第一件事就是定義一個接口,這個接口決定組件如何和service交互。而這個接口需要通過IBinder實現,並且這個接口通過onBind()返回。

     一次有多個組件綁定到service。組件和service交互完,需要調用unbindService()去解綁。

運行一個Service在前端進程

     一個前端進程的service是用戶特別關心的,因此當內存低的時候,不希望系統殺死他。一個前端service必須提供一個通知給狀態欄,在“前進欄”的下面,意味着,這個通知不能消失,直到service停止或者從前端狀態移除。
     比如,一個音樂播放器,播放音樂的的服務應該在前端進程,因爲使用者明確關心這個操作。通知欄可以顯示當前的歌曲並且允許使用者登錄到Activity操作播放器。

     運行在前端的操作是調用startForeground()。這個方法帶有兩個參數:一個是整型對應通知,還有一個是狀態欄的通知。     比如:

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);

警告:startForeground的整型ID必須爲0

     調用stopForeground()將service從前端進程移除。有個boolean變量,指明這條狀態是否也移除。這個方法不會停止service。然而,如果當在前端進程運行的時候停止了service,這個通知也會移除。

 管理Service的生命週期

     service的生命週期—從何時創建到何時銷燬--遵循以下兩種不同的途徑。
     
started service
     調用startService()創建service。service一直 運行在後臺,直到調用stopSelf()。或者其他組件調用stopService()。service停止了,系統會銷燬他。
     
bound service
     調用bindService()創建service。所有綁定這個service的組件都解綁了,service就銷燬。

     這兩種方式不是完全獨立的。可以通過startService啓動。比如,一個後臺音樂service通過startService啓動。後來,用戶想通過測試這個播放器或者獲得歌的信息,一個Activity和這個service通過bindService()綁定。這種情況下,stopService()後者stopSelf()不會停止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
    }
}


通過實現這些方法,可以監測兩個內部循環的service生命週期:

entire lifetime
     entire lifetime發生在onCreate()和onDestroy()之間。     
active lifetime
     active lifetime開始於onStartCommand()或者onBind()。每一個方法處理的Intent被傳遞到startService()或者bindService()。
     如果service開始了,active lifetime結局了,那麼entire lifetime也會結束。(onStartCommand()返回後,service任然活動的)。如果service解綁了,當onUnbind()返回,active lifetime就結束了。

圖上闡明瞭service的回調的特徵。雖然圖將startService和bindService區別開來,記住,任何service它是怎麼啓動的,組件都可以綁定它。所以一個service以onStartCommand啓動(通過調用startService())任然可以接受onBind()(當一個組件調用了bindService)。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章