android service 之二(IntentService)

不管是何種Service,它默認都是在應用程序的主線程(亦即UI線程)中運行的。所以,如果你的Service將要運行非常耗時或者可能被阻塞的操作時,你的應用程序將會被掛起,甚至會出現ANR錯誤。為了避免這一問題,你應該在Service中重新啟動一個新的線程來進行這些操作。現有兩種方法共大家參考:

① 直接在Service的onStartCommand()方法中重啟一個線程來執行,如:

Java代碼  收藏代碼
  1. @Override  
  2.     public int onStartCommand(Intent intent, int flags, int startId) {  
  3.         MyServiceActivity.updateLog(TAG + " ----> onStartCommand()");  
  4.         new Thread(new Runnable() {  
  5.             @Override  
  6.             public void run() {  
  7.                 // 此處進行耗時的操作,這裡只是簡單地讓線程睡眠了1s  
  8.                 try {  
  9.                     Thread.sleep(1000);  
  10.                 } catch (Exception e) {  
  11.                     e.printStackTrace();  
  12.                 }  
  13.             }  
  14.         }).start();  
  15.         return START_STICKY;  
  16.     }  

 ② Android SDK 中為我們提供了一個現成的Service類來實現這個功能,它就是IntentService,它主要負責以下幾個方面:

   

  • Creates a default worker thread that executes all intents delivered to onStartCommand() separate from your application's main thread.
  •     生成一個默認的且與主線程互相獨立的工作者線程來執行所有傳送至 onStartCommand() 方法的Intetnt

  • Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading.
  •     生成一個工作隊列來傳送Intent對象給你的onHandleIntent()方法,同一時刻只傳送一個Intent對象,這樣一來,你就不必擔心多線程的問題。

  • Stops the service after all start requests have been handled, so you never have to call stopSelf().
  •     在所有的請求(Intent)都被執行完以後會自動停止服務,所以,你不需要自己去調用stopSelf()方法來停止該服務

  • Provides default implementation of onBind() that returns null.
  •     提供了一個onBind()方法的默認實現,它返回null

  • Provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation
  •     提供了一個onStartCommand()方法的默認實現,它將Intent先傳送至工作隊列,然後從工作隊列中每次取出一個傳送至onHandleIntent()方法,在該方法中對Intent對相應的處理

     

    以上,英文來自官方SDK,中文為我所譯。

     

    從以上看來,你所需要做的就是實現 onHandleIntent() 方法,在該方法內實現你想進行的操作。另外,繼承IntentService時,你必須提供一個無參構造函數,且在該構造函數內,你需要調用父類的構造函數,如下:

    Java代碼  收藏代碼
    1. public HelloIntentService() {        
    2.     super("HelloIntentService");    
    3. }  

      

    下面給出一例,來解釋一下:

    Java代碼  收藏代碼
    1. // activity 的onCreate()  
    2. @Override  
    3.     public void onCreate(Bundle savedInstanceState) {  
    4.         super.onCreate(savedInstanceState);  
    5.         setContentView(R.layout.main);  
    6.   
    7.         startSer1 = (Button) findViewById(R.id.startSer1);  
    8.         stopSer1 = (Button) findViewById(R.id.stopSer1);  
    9.   
    10.         startSer2 = (Button) findViewById(R.id.startSer2);  
    11.         stopSer2 = (Button) findViewById(R.id.stopSer2);  
    12.   
    13.         log = (TextView) findViewById(R.id.log);  
    14.   
    15.         logView = (ScrollView) findViewById(R.id.logView);  
    16.   
    17.         startSer1.setOnClickListener(btnListener);  
    18.         stopSer1.setOnClickListener(btnListener);  
    19.   
    20.         startSer2.setOnClickListener(btnListener);  
    21.         stopSer2.setOnClickListener(btnListener);  
    22.   
    23.         intent = new Intent(MyServiceActivity.this, IntentServiceDemo.class);  
    24.   
    25.         // 打印出主線程的ID  
    26.         long id = Thread.currentThread().getId();  
    27.         updateLog(TAG + " ----> onCreate() in thread id: " + id);  
    28.     }  

     

    Java代碼  收藏代碼
    1. // service 代碼  
    2. package com.archer.rainbow;  
    3.   
    4. import java.text.SimpleDateFormat;  
    5. import java.util.Date;  
    6.   
    7. import android.app.IntentService;  
    8. import android.content.Intent;  
    9.   
    10. public class IntentServiceDemo extends IntentService {  
    11.     private static final String TAG = "IntentServiceDemo";  
    12.     private static final SimpleDateFormat SDF_DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.SSS");  
    13.   
    14.     public IntentServiceDemo() {  
    15.         super(TAG);  
    16.         MyServiceActivity.updateLog(TAG + " ----> constructor");  
    17.     }  
    18.   
    19.     @Override  
    20.     public void onCreate() {  
    21.         super.onCreate();  
    22.   
    23.         // 打印出該Service所在線程的ID  
    24.         long id = Thread.currentThread().getId();  
    25.         MyServiceActivity.updateLog(TAG + " ----> onCreate() in thread id: "  
    26.                 + id);  
    27.     }  
    28.   
    29.     @Override  
    30.     public void onDestroy() {  
    31.         super.onDestroy();  
    32.         MyServiceActivity.updateLog(TAG + " ----> onDestroy()");  
    33.     }  
    34.   
    35.     @Override  
    36.     public int onStartCommand(Intent intent, int flags, int startId) {  
    37.         MyServiceActivity.updateLog(TAG + " ----> onStartCommand()");  
    38.         // 記錄發送此請求的時間  
    39.         intent.putExtra("time", System.currentTimeMillis());  
    40.         return super.onStartCommand(intent, flags, startId);  
    41.     }  
    42.   
    43.     @Override  
    44.     public void setIntentRedelivery(boolean enabled) {  
    45.         MyServiceActivity.updateLog(TAG + " ----> setIntentRedelivery()");  
    46.         super.setIntentRedelivery(enabled);  
    47.     }  
    48.   
    49.     @Override  
    50.     protected void onHandleIntent(Intent intent) {  
    51.         // 打印出處理intent所用的線程的ID  
    52.         long id = Thread.currentThread().getId();  
    53.         MyServiceActivity.updateLog(TAG  
    54.                 + " ----> onHandleIntent() in thread id: " + id);  
    55.         long time = intent.getLongExtra("time"0);  
    56.         Date date = new Date(time);  
    57.         try {  
    58.             // 打印出每個請求對應的觸發時間  
    59.             MyServiceActivity.updateLog(TAG  
    60.                     + " ----> onHandleIntent(): 下載文件中..." + SDF_DATE_FORMAT.format(date));  
    61.             Thread.sleep(3000);  
    62.         } catch (InterruptedException e) {  
    63.             e.printStackTrace();  
    64.         }  
    65.     }  
    66.   
    67. }  

     應用啟動時,界面如下:




     從此圖可以看出,主線程(UI線程)的ID是1。接,連續點擊三次Start Service 1 按鈕,得如下畫面:



     

     從此圖中可以看出,IntentServiceDemo的onCreate()所處的線程ID仍為1,說明它是在主線程中被執行的,且只被執行一次。然後,我每點擊一次按鈕,它都會觸發一下onStartCommand()方法。仔細看第二次與第三次的onCommand()方法以及onHandleIntent()打印出來的語句,你會發現,第二、三兩次點擊按鈕與第一次點擊按鈕的時間是沒有超過3秒鐘的,它們是連續被執行的,這說明瞭什麼呢?說明,在第一個intent被處理時(即onHandleIntent()處於運行中),該Service仍然可以接受新的請求,但接受到新的請求後並沒有立即執行,而是將它們放入了工作隊列中,等待被執行。

     

    這就是 IntentService 的簡單用法。但你若是想在Service中讓多個線程併發的話,就得另想法子嘍。比如,使用第一種方法,在Service內部起多個線程,但是這樣的話,你可要處理好線程的同步哦~~~