TShopping

 找回密碼
 註冊
搜索
查看: 1321|回復: 0
打印 上一主題 下一主題

[教學] Android中startService的使用及Service生命週期

[複製鏈接]
跳轉到指定樓層
1#
發表於 2018-7-19 15:08:16 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
 
Push to Facebook
Android中有兩種主要方式使用Service,通過調用Context的startService方法或調用Context的bindService方法,本文只探討純startService的使用,不涉及任何bindService方法調用的情況。如果想了解bindService的相關使用,請參見《Android中bindService的使用及Service生命週期》。

當我們通過調用了Context的startService方法後,我們便啟動了Service,通過startService方法啟動的Service會一直無限期地運行下去,只有在外部調用Context的stopService或Service內部調用Service的stopSelf方法時,該Service才會停止運行並銷毀。

要想使用Service,首先我們要繼承自Service,然後重寫如下方法:
onCreate, onStartCommand, onBind和onDestroy。

這幾個方法都是回調方法,都是由Android操作系統在合適的時機調用的,並且需要注意的是這幾個回調方法都是在主線程中被調用的。

onCreate :執行startService方法時,如果Service沒有運行的時候會創建該Service並執行Service的onCreate回調方法;如果Service已經處於運行中,那麼執行startService方法不會執行Service的onCreate方法。也就是說如果多次執行了Context的startService方法啟動Service,Service方法的onCreate方法只會在第一次創建Service的時候調用一次,以後均不會再次調用。我們可以在onCreate方法中完成一些Service初始化相關的操作。

onStartCommand :在執行了startService方法之後,有可能會調用Service的onCreate方法,在這之後一定會執行Service的onStartCommand回調方法。也就是說,如果多次執行了Context的startService方法,那麼Service的onStartCommand方法也會相應的多次調用。onStartCommand方法很重要,我們在該方法中根據傳入的Intent參數進行實際的操作,比如會在此處創建一個線程用於下載數據或播放音樂等。

onBind : Service中的onBind方法是抽象方法,所以Service類本身就是抽像類,也就是onBind方法是必須重寫的,即使我們用不到。在通過startService使用Service時,我們在重寫onBind方法時,只需要將其返回null即可。onBind方法主要是用於給bindService方法調用Service時才會使用到。

onDestroy :通過startService方法啟動的Service會無限期運行,只有當調用了Context的stopService或在Service內部調用stopSelf方法時,Service才會停止運行並銷毀,在銷毀的時候會執行Service回調函數。

我們為了探究通過startService方法啟動的Service的生命週期以驗證上面對各個回調函數方法的描述,寫瞭如下的一個測試案例。
首先創建一個服務類TestService,該類繼承自Service,代碼如下:
  1. package com.ispring.startservicedemo;

  2. import android.app.Service;
  3. import android.content.Intent;
  4. import android.os.IBinder;
  5. import android.util.Log;

  6. public class TestService extends Service {

  7.     @Override
  8.     public void onCreate() {
  9.         Log.i("DemoLog","TestService -> onCreate, Thread ID: " + Thread.currentThread().getId());
  10.         super.onCreate();
  11.     }

  12.     @Override
  13.     public int onStartCommand(Intent intent, int flags, int startId) {
  14.         Log.i("DemoLog", "TestService -> onStartCommand, startId: " + startId + ", Thread ID: " + Thread.currentThread().getId());
  15.         return START_STICKY;
  16.     }

  17.     @Override
  18.     public IBinder onBind(Intent intent) {
  19.         Log.i("DemoLog", "TestService -> onBind, Thread ID: " + Thread.currentThread().getId());
  20.         return null;
  21.     }

  22.     @Override
  23.     public void onDestroy() {
  24.         Log.i("DemoLog", "TestService -> onDestroy, Thread ID: " + Thread.currentThread().getId());
  25.         super.onDestroy();
  26.     }
  27. }
複製代碼

我們在TestService的各個回調方法中只是簡單打印出了相應的信息,並沒有做很多複雜的處理操作。

然後我們在Activity中調用該Serivce,Activity中相應的代碼如下:

  1. package com.ispring.startservicedemo;

  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.util.Log;


  6. public class MainActivity extends Activity {

  7.     @Override
  8.     protected void onCreate(Bundle savedInstanceState) {
  9.         super.onCreate(savedInstanceState);
  10.         setContentView(R.layout.activity_main);

  11.         Log.i("DemoLog", "Thread ID: " + Thread.currentThread().getId());

  12.         Log.i("DemoLog", "before test startService");

  13.         //连续启动Service
  14.         Intent intent1 = new Intent(this, TestService.class);
  15.         startService(intent1);
  16.         Intent intent2 = new Intent(this, TestService.class);
  17.         startService(intent2);
  18.         Intent intent3 = new Intent(this, TestService.class);
  19.         startService(intent3);

  20.         //停止Service
  21.         Intent intent4 = new Intent(this, TestService.class);
  22.         stopService(intent4);

  23.         //再次启动Service
  24.         Intent intent5 = new Intent(this, TestService.class);
  25.         startService(intent5);

  26.         Log.i("DemoLog", "after test startService");
  27.     }
  28. }
複製代碼

我們在Activity中,首先連續三次調用了Activity的startService方法以啟動Service,然後調用Activity的stopService方法停止Service,然後又通過調用Activity的startService方法啟動Service。

運行程序的輸出結果如下:



我們分析一下上面的輸出結果,首先打印出了主線程的ID是1, 然後我們發現後面所有在回調函數中打印出的執行線程的ID也就是1,這就說明了Service中的各個回調方法是運行在主線程中的。其次我們可以發現在我們連續調用了三次startService方法之後,只觸發了一次onCreate回調方法,觸發了三次onStartCommand方法,在onStartCommand中我們可以讀取到通過startService方法傳入的Intent對象,並且這三次的startId都不同,分別是1,2,3,每次調用startService都會自動分配一個startId,startId可以用來區分不同的startService的調用,一般情況下startId都是從1開始計數,以後每次調用startService之後startId自動加一遞增。

之後我們又調用了Activity的stopService(intent4)方法用於停止Service,通過輸出結果我們發現Service執行了onDestroy方法,一般情況下我們可以在onDestroy方法中執行一些資源釋放的操作。執行完onDestroy之後該Service的實例就銷毀了。雖然我們之前調用了三次startService方法,但是只要調用一次stopService就可以讓運行中的Service停止運行並銷毀。

最後我們再次通過startService(intent5)啟動Service時,通過輸出結果我們發現再次執行了Service的onCreate方法,這說明Service在通過stopService銷毀之後重新創建了,並隨之再次調用onStartCommand回調方法,並且startId再次從1開始計數。

最後需要注意的是我們在Activity中操作Service的開始和結尾處分別寫了兩句輸出代碼,分別是

  1. Log.i("DemoLog", "before test startService");
複製代碼



  1. Log.i("DemoLog", "after test startService");
複製代碼

但是我們再看一下輸出結果會發現,程序直接上來在輸出了before test startService之後,卻立即輸出了after test startService,在這之後才是TestService內部各個回調方法的輸出,這說明startService()方法和stopService ()方法在執行完後立即返回了,也就是這兩個方法都不是阻塞式的,啟動service和停止service都是異步操作,startService()、stopService()都是將intent對象發送給Android Framework,然後Framework層異步地啟動、停止Service。

我們用一張圖來概括一下通過startService啟動的Service的生命週期:


當Android面臨內存匱乏的時候,可能會銷毀掉你當前運行的Service,然後待內存充足的時候可以重新創建Service,Service被Android系統強制銷毀並再次重建的行為依賴於Service中onStartCommand方法的返回值。我們常用的返回值有三種值,START_NOT_STICKY、START_STICKY和START_REDELIVER_INTENT,這三個值都是Service中的靜態常量。

START_NOT_STICKY:如果返回START_NOT_STICKY,表示當Service運行的進程被Android系統強制殺掉之後,不會重新創建該Service,當然如果在其被殺掉之後一段時間又調用了startService,那麼該Service又將被實例化。那什麼情境下返回該值比較恰當呢?如果我們某個Service執行的工作被中斷幾次無關緊要或者對Android內存緊張的情況下需要被殺掉且不會立即重新創建這種行為也可接受,那麼我們便可將onStartCommand的返回值設置為START_NOT_STICKY。舉個例子,某個Service需要定時從服務器獲取最新數據:通過一個定時器每隔指定的N分鐘讓定時器啟動Service去獲取服務端的最新數據。當執行到Service的onStartCommand時,在該方法內再規劃一個N分鐘後的定時器用於再次啟動該Service並開闢一個新的線程去執行網絡操作。假設Service在從服務器獲取最新數據的過程中被Android系統強制殺掉,Service不會再重新創建,這也沒關係,因為再過N分鐘定時器就會再次啟動該Service並重新獲取數據。

START_STICKY:如果返回START_STICKY,表示Service運行的進程被Android系統強制殺掉之後,Android系統會將該Service依然設置為started狀態(即運行狀態),但是不再保存onStartCommand方法傳入的intent對象,然後Android系統會嘗試再次重新創建該Service,並執行onStartCommand回調方法,但是onStartCommand回調方法的Intent參數為null,也就是onStartCommand方法雖然會執行但是獲取不到intent信息。如果你的Service可以在任意時刻運行或結束都沒什麼問題,而且不需要intent信息,那麼就可以在onStartCommand方法中返回START_STICKY,比如一個用來播放背景音樂功能的Service就適合返回該值。

START_REDELIVER_INTENT:如果返回START_REDELIVER_INTENT,表示Service運行的進程被Android系統強制殺掉之後,與返回START_STICKY的情況類似,Android系統會將再次重新創建該Service,並執行onStartCommand回調方法,但是不同的是,Android系統會再次將Service在被殺掉之前最後一次傳入onStartCommand方法中的Intent再次保留下來並再次傳入到重新創建後的Service的onStartCommand方法中,這樣我們就能讀取到intent參數。只要返回START_REDELIVER_INTENT,那麼onStartCommand重的intent一定不是null。如果我們的Service需要依賴具體的Intent才能運行(需要從Intent中讀取相關數據信息等),並且在強制銷毀後有必要重新創建運行,那麼這樣的Service就適合返回START_REDELIVER_INTENT。



原文出處

 

臉書網友討論
*滑块验证:
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2024-4-24 02:19 , Processed in 0.085885 second(s), 25 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表