Android學習筆記16——Service(2)

接下來我們開始學習如何在Manifest文件中定義一個Service

<span style="font-size:14px;"><manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest></span>

我們可以在Service這個元素中定義一些屬性例如啓動這個Service所需要的權限或者這個Service將要運行的進程。而android:name是僅僅必須的——它用來指定這個Service的類名稱。一旦你發佈了自己的應用,這個Service的名稱就不能改變,因爲這樣可能影響到其他將要調用這個Service的應用。有的時候爲了確保我們的app的安全,我們在啓動或者綁定一個Service的時候定義一個精確的Service而不會定義這個Service的Intent Filter。另外,如果我們想要我們自己的程序運行我們的Serivce,我們可以通過定義andriod:exported = false。這樣將有效的阻止其他應用程序啓動這個Service,甚至於通過一個不精確的Intent的。

創建一個已經啓動的Service

一個已經啓動的Service就是其他的組件調用startService方法或者bindService方法啓動並且調用這個Service的onStartCommond方法的Service。

當一個Service被啓動的時候,它就會擁有獨立於啓動這個Service的組件的生命週期,並且這個Service能夠獨立運行在後臺,即使這個組件被銷燬掉。這個Service將會一直運行下去直到這個Service自己調用stopSelf方法,或者另一個組件調用stopService方法停止這個Service。

一個應用程序的組件例如一個Activity能夠通過通過startService方法來開啓這個Service,並且向這個Service傳遞一個Intent並且包含一些這個Service要使用的數據,而這個Service將會在onStartCommond方法中接受到這個Intent。

在這裏我們可以通過繼承Service和IntentService類來定義一個自己的Service。

Service:這個是所有Service的父類。當我們使用這個Service的時候,我們需要自己開啓開啓一個新的線程去完成這個Service要做的工作。因爲這個Service默認的使用這個應用程序的主線程,這樣將要減慢應用程序的速度。

IntentService:這是一個Service的子類,它用一個工作線程去完成所有請求的任務,但是一次只能執行一個請求。這樣,這個IntentService就成爲了我們最好的選擇,如果我們的Service不要求多線程工作的話。我們所需要做的是實現onHandleIntent方法,在這個方法中我們接受到每個請求的Intent,這樣就能夠在後臺工作。

繼承IntentService類

因爲大多數被啓動的Service不需要同時進行多個請求(這也是一種非常危險的多線程方案),如此一來,繼承IntentService類來定義自己的Service可能成爲一種最好的方式。

IntentService做了以下操作:

1、創建一個默認的工作線程用來執行所有的發送給onStartCommond方法的Intent,這個線程是獨立於這個應用的主線程的。

2、創建一個工作隊列,這個隊列一次只能傳遞一個Intent給onHandleIntent方法的實現,因此我們不需要擔心多線程的問題。

3、當所有的請求的Intent進行完成以後,這個Service自動停止,我們不需要調用stopSelf方法來停止這個Service。

4、提供onBind的默認實現,返回一個NULL。

5、提供onStartCommond方法的默認實現,發送Intent到工作隊列,然後傳遞給onHandleIntent方法的默認實現。

<span style="font-size:14px;">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) {
              }
          }
      }
  }
}</span>
有以上代碼我們可以看出我們需要做的僅是:提供一個構造方法並且實現onHandleIntent方法。

但是如果我們也想要實現其他方法,我們必須記得首先調用他們父類的實現,以至於IntentService能夠正確的操作這個工作線程的生命週期。

<span style="font-size:14px;">@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);
}</span>

除了onHandleIntent方法以外,我們不需要調用父類實現的唯一的方法是onBind()方法。

繼承Service類

正如上面的例子一樣,繼承IntentService將會事我們自定義的Service更加簡單。然而,如果我們希望我們的Service能夠表現多線程操作,而不是通過工作隊列,那麼我們需要繼承Service來操作每一個Intent。

作爲比較,下面的代碼實例是一個繼承了Service類並且進行和IntentService相同操作,對於每一個請求都用一個工作線程去完成,一次僅僅進行一個請求。

<span style="font-size:14px;">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();
  }
}</span>

真如我們看到的,它做了比IntentService更多的工作才能完成相同的任務。

然而,正是因爲我們操作了每一個請求,這樣我們也就可以同時進行多個請求的操作。如果我們想到達到這個目的,我們可以爲每一個Intent啓動一個新的線程來完成不同的工作。

注意onStartCommond方法必須返回一個int類型的數值,而這個數值描述了當系統殺死這個Service時的時候,系統將要怎麼進行這個Service。返回的常量必須是以下幾種數值:

START_NOT_STICKY:這個數值代表如果系統殺死了這個Service,這個Service將要不會重新創建,除非有一個PendingIntent被髮送。這是最安全的方式,避免了當我們的Service在不需要或者僅僅需要重新啓動未完成的任務的時候運行。

START_STICKY:這個數值代表,當系統殺死這個Service的時候,系統會重新創建這個Service,並且調用onStartCommond方法,但是不會重新發送最後一個Intent。相反,系統用一個null的Intent調用onStartCommond方法,除非有一些pendingIntent來開啓這個Service,在這種情況下這些Service被髮送。這種方式適合於一個音樂播放器,不執行命令只是在後臺運行並且等待着一個工作。

START_REDELIVER_INTENT:這個數值代表如果系統殺死了這個Service,系統會重新創建這個Serivice,並且用發送給這個Service的最後一個Intent來調用onStartCommond方法。任何pendingIntent被依次發送。這適合於需要立即恢復的工作,例如文件下載。






發佈了43 篇原創文章 · 獲贊 25 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章