1. 程式人生 > >Android中Service(服務)和Thread(執行緒)的關係

Android中Service(服務)和Thread(執行緒)的關係

一、Service(服務)

Service是Android中四大元件之一,在Android開發中起到非常重要的作用,先來看一下官方對Service的定義:

 is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.


翻譯過來就是:Service(服務)是一個沒有使用者介面的在後臺執行執行耗時操作的應用元件。其他應用元件能夠啟動Service,並且當用戶切換到另外的應用場景,Service將持續在後臺執行。另外,一個元件能夠繫結到一個service與之互動(IPC機制),例如,一個service可能會處理網路操作,播放音樂,操作檔案I/O或者與內容提供者(content provider)互動,所有這些活動都是在後臺進行。

Service有兩種狀態,“啟動的”和“繫結”

Started A service is "started" when an application component (such as an activity) starts it by calling . Once started, a service can run in the background indefinitely, even if the component that started it is destroyed. Usually, a started service performs a single operation and does not return a result to the caller. For example, it might download or upload a file over the network. When the operation is done, the service should stop itself.
Bound

A service is "bound" when an application component binds to it by calling . A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.

通過startService()啟動的服務處於“啟動的”狀態,一旦啟動,service就在後臺執行,即使啟動它的應用元件已經被銷燬了。通常started狀態的service執行單任務並且不返回任何結果給啟動者。比如當下載或上傳一個檔案,當這項操作完成時,service應該停止它本身。

還有一種“繫結”狀態的service,通過呼叫bindService()來啟動,一個繫結的service提供一個允許元件與service互動的介面,可以傳送請求、獲取返回結果,還可以通過誇程序通訊來互動(IPC)。繫結的service只有當應用元件繫結後才能執行,多個元件可以繫結一個service,當呼叫unbind()方法時,這個service就會被銷燬了。

另外,在官方的說明文件中還有一個警告:

Caution: A service runs in the main thread of its hosting process—the service does not create its own thread and does not run in a separate process (unless you specify otherwise). This means that, if your service is going to do any CPU intensive work or blocking operations (such as MP3 playback or networking), you should create a new thread within the service to do that work. By using a separate thread, you will reduce the risk of Application Not Responding (ANR) errors and the application's main thread can remain dedicated to user interaction with your activities.

意思是service與activity一樣都存在與當前程序的主執行緒中,所以,一些阻塞UI的操作,比如耗時操作不能放在service裡進行,比如另外開啟一個執行緒來處理諸如網路請求的耗時操作。如果在service裡進行一些耗CPU和耗時操作,可能會引發ANR警告,這時應用會彈出是強制關閉還是等待的對話方塊。所以,對service的理解就是和activity平級的,只不過是看不見的,在後臺執行的一個元件,這也是為什麼和activity同被說為Android的基本元件。

Service生命週期中的一些方法:

                        

通過這個圖可以看到,兩種啟動service的方式以及他們的生命週期,bind service的不同之處在於當繫結的元件銷燬後,對應的service也就被kill了。service的宣告週期相比與activity的簡單了許多,只要好好理解兩種啟動service方式的異同就行。

service生命週期也涉及一些回撥方法,這些方法都不用呼叫父類方法,具體如下:

  1. <span style="font-family:Comic Sans MS;font-size:18px;">publicclass ExampleService extends Service {  
  2.     int mStartMode;       // indicates how to behave if the service is killed
  3.     IBinder mBinder;      // interface for clients that bind
  4.     boolean mAllowRebind; // indicates whether onRebind should be used
  5.     @Override
  6.     publicvoid onCreate() {  
  7.         // The service is being created
  8.     }  
  9.     @Override
  10.     publicint onStartCommand(Intent intent, int flags, int startId) {  
  11.         // The service is starting, due to a call to startService()
  12.         return mStartMode;  
  13.     }  
  14.     @Override
  15.     public IBinder onBind(Intent intent) {  
  16.         // A client is binding to the service with bindService()
  17.         return mBinder;  
  18.     }  
  19.     @Override
  20.     publicboolean onUnbind(Intent intent) {  
  21.         // All clients have unbound with unbindService()
  22.         return mAllowRebind;  
  23.     }  
  24.     @Override
  25.     publicvoid onRebind(Intent intent) {  
  26.         // A client is binding to the service with bindService(),
  27.         // after onUnbind() has already been called
  28.     }  
  29.     @Override
  30.     publicvoid onDestroy() {  
  31.         // The service is no longer used and is being destroyed
  32.     }  
  33. }</span>  

關於Service生命週期還有一張比較易懂的圖(來源於網路)


另外,這裡要說明Service的一個子類,IntentService,首先看下官方文件的說明:

This is a subclass of  that uses a worker thread to handle all start requests, one at a time. This is the best option if you don't require that your service handle multiple requests simultaneously. All you need to do is implement , which receives the intent for each start request so you can do the background work.

IntentService使用佇列的方式將請求的Intent加入佇列,然後開啟一個worker thread(執行緒)來處理佇列中的Intent,對於非同步的startService請求,IntentService會處理完成一個之後再處理第二個,每一個請求都會在一個單獨的worker thread中處理,不會阻塞應用程式的主執行緒,這裡就給我們提供了一個思路,如果有耗時的操作與其在Service裡面開啟新執行緒還不如使用IntentService來處理耗時操作。而在一般的繼承Service裡面如果要進行耗時操作就必須另開執行緒,但是使用IntentService就可以直接在裡面進行耗時操作,因為預設實現了一個worker thread。對於非同步的startService請求,IntentService會處理完成一個之後再處理第二個。

看下IntentService的具體實現:

  1. <span style="font-family:Comic Sans MS;font-size:18px;color:#222222;">publicclass HelloIntentService extends IntentService {  
  2.   /**  
  3.    * A constructor is required, and must call the super IntentService(String) 
  4.    * constructor with a name for the worker thread. 
  5.    */
  6.   public HelloIntentService() {  
  7.       super("HelloIntentService");  
  8.   }  
  9.   /** 
  10.    * The IntentService calls this method from the default worker thread with 
  11.    * the intent that started the service. When this method returns, IntentService 
  12.    * stops the service, as appropriate. 
  13.    */
  14.   @Override
  15.   protectedvoid onHandleIntent(Intent intent) {  
  16.       // Normally we would do some work here, like download a file.
  17.       // For our sample, we just sleep for 5 seconds.
  18.       long endTime = System.currentTimeMillis() + 5*1000;  
  19.       while (System.currentTimeMillis() < endTime) {  
  20.           synchronized (this) {  
  21.               try {  
  22.                   wait(endTime - System.currentTimeMillis());  
  23.               } catch (Exception e) {  
  24.               }  
  25.           }  
  26.       }  
  27.   }  
  28. }</span>  

關於停止Service,如果service是非繫結的,最終當任務完成時,為了節省系統資源,一定要停止service,可以通過stopSelf()來停止,也可以在其他元件中通過stopService()來停止,繫結的service可以通過onUnBind()來停止service。

二、

1). Thread:Thread 是程式執行的最小單元,它是分配CPU的基本單位。可以用 Thread 來執行一些非同步的操作。

2). Service:Service 是android的一種機制,當它執行的時候如果是Local Service,那麼對應的 Service 是執行在主程序的 main 執行緒上的。如:onCreate,onStart 這些函式在被系統呼叫的時候都是在主程序的 main 執行緒上執行的。如果是Remote Service,那麼對應的 Service 則是執行在獨立程序的 main 執行緒上。因此請不要把 Service 理解成執行緒,它跟執行緒半毛錢的關係都沒有!

既然這樣,那麼我們為什麼要用 Service 呢?其實這跟 android 的系統機制有關,我們先拿 Thread 來說。Thread 的執行是獨立於 Activity 的,也就是說當一個 Activity 被 finish 之後,如果你沒有主動停止 Thread 或者 Thread 裡的 run 方法沒有執行完畢的話,Thread 也會一直執行。因此這裡會出現一個問題:當 Activity 被 finish 之後,你不再持有該 Thread 的引用。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制。

舉個例子:如果你的 Thread 需要不停地隔一段時間就要連線伺服器做某種同步的話,該 Thread 需要在 Activity 沒有start的時候也在執行。這個時候當你 start 一個 Activity 就沒有辦法在該 Activity 裡面控制之前建立的 Thread。因此你便需要建立並啟動一個 Service ,在 Service 裡面建立、執行並控制該 Thread,這樣便解決了該問題(因為任何 Activity 都可以控制同一 Service,而系統也只會建立一個對應 Service 的例項)。

因此你可以把 Service 想象成一種訊息服務,而你可以在任何有 Context 的地方呼叫 Context.startService、Context.stopService、Context.bindService,Context.unbindService,來控制它,你也可以在 Service 裡註冊 BroadcastReceiver,在其他地方通過傳送 broadcast 來控制它,當然這些都是 Thread 做不到的。

Service的生命週期(onCreate  onStart  onDestroy  onBind  )

1). 被啟動的服務的生命週期:如果一個Service被某個Activity 呼叫 Context.startService 方法啟動,那麼不管是否有Activity使用bindService繫結或unbindService解除繫結到該Service,該Service都在後臺執行。如果一個Service被startService 方法多次啟動,那麼onCreate方法只會呼叫一次,onStart將會被呼叫多次(對應呼叫startService的次數),並且系統只會建立Service的一個例項(因此你應該知道只需要一次stopService呼叫)。該Service將會一直在後臺執行,而不管對應程式的Activity是否在執行,直到被呼叫stopService,或自身的stopSelf方法。當然如果系統資源不足,android系統也可能結束服務。

2). 被繫結的服務的生命週期:如果一個Service被某個Activity 呼叫 Context.bindService 方法繫結啟動,不管呼叫 bindService 呼叫幾次,onCreate方法都只會呼叫一次,同時onStart方法始終不會被呼叫。當連線建立之後,Service將會一直執行,除非呼叫Context.unbindService 斷開連線或者之前呼叫bindService 的 Context 不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被呼叫。

3). 被啟動又被繫結的服務的生命週期:如果一個Service又被啟動又被繫結,則該Service將會一直在後臺執行。並且不管如何呼叫,onCreate始終只會呼叫一次,對應startService呼叫多少次,Service的onStart便會呼叫多少次。呼叫unbindService將不會停止Service,而必須呼叫 stopService 或 Service的 stopSelf 來停止服務。

4). 當服務被停止時清除服務:當一個Service被終止(1、呼叫stopService;2、呼叫stopSelf;3、不再有繫結的連線(沒有被啟動))時,onDestroy方法將會被呼叫,在這裡你應當做一些清除工作,如停止在Service中建立並執行的執行緒。

特別注意:

1、你應當知道在呼叫 bindService 繫結到Service的時候,你就應當保證在某處呼叫 unbindService 解除繫結(儘管 Activity 被 finish 的時候繫結會自      動解除,並且Service會自動停止);

2、你應當注意 使用 startService 啟動服務之後,一定要使用 stopService停止服務,不管你是否使用bindService; 

3、同時使用 startService 與 bindService 要注意到,Service 的終止,需要unbindService與stopService同時呼叫,才能終止 Service,不管 startService 與 bindService 的呼叫順序,如果先呼叫 unbindService 此時服務不會自動終止,再呼叫 stopService 之後服務才會停止,如果先呼叫 stopService 此時服務也不會終止,而再呼叫 unbindService 或者 之前呼叫 bindService 的 Context 不存在了(如Activity 被 finish 的時候)之後服務才會自動停止;

4、當在旋轉手機螢幕的時候,當手機螢幕在“橫”“豎”變換時,此時如果你的 Activity 如果會自動旋轉的話,旋轉其實是 Activity 的重新建立,因此旋轉之前的使用 bindService 建立的連線便會斷開(Context 不存在了),對應服務的生命週期與上述相同。

5、在 sdk 2.0 及其以後的版本中,對應的 onStart 已經被否決變為了 onStartCommand,不過之前的 onStart 任然有效。這意味著,如果你開發的應用程式用的 sdk 為 2.0 及其以後的版本,那麼你應當使用 onStartCommand 而不是 onStart。

6、Service 是android的一種機制,當它執行的時候如果是Local Service,那麼對應的 Service 是執行在主程序的 main 執行緒上的。如果是Remote Service,那麼對應的 Service 則是執行在獨立程序的 main 執行緒上,service和呼叫者之間的通訊都是同步的(不論是遠端service還是本地service),它跟執行緒一點關係都沒有!

7、Thread 的執行是獨立於 Activity 的,也就是說當一個 Activity 被 finish 之後,如果你沒有主動停止 Thread 或者 Thread 裡的 run 方法沒有執行完畢的話,Thread 也會一直執行。因此這裡會出現一個問題:當 Activity 被 finish 之後,你不再持有該 Thread 的引用,也就是你下次啟動的時候,無法控制之前建立的執行緒,而service則可以。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制。

8、你也可以在 Service 裡註冊 BroadcastReceiver,在其他地方通過傳送 broadcast 來控制它,這些是 Thread 做不到的。