Android服務Service總結

轉自 http://blog.csdn.net/liuhe688/article/details/6874378html

富貴必從勤苦得,男兒須讀五車書。唐.杜甫《柏學士茅屋》java

做爲程序員的咱們,須知富貴是要經過勤苦努力才能獲得的,要想在行業內有所建樹,就必須刻苦學習和鑽研。android

今天咱們來說一下Android中Service的相關內容。程序員

Service在Android中和Activity是屬於同一級別上的組件,咱們能夠將他們認爲是兩個好哥們,Activity儀表不凡,迷倒萬千少女,常常作一些公衆人物角色,而Service一副彪悍的長相,但卻身強力壯,經常在後臺作一些搬運工的力氣活,雖然有些累,但你們都不能失去他。app

下面咱們就圍繞Service對其進行全面講解:異步

1.Service生命週期ide

Service生命週期能夠從兩種啓動Service的模式開始講起,分別是context.startService()和context.bindService()學習

(1).startService的啓動模式下的生命週期:當咱們首次使用startService啓動一個服務時,系統會實例化一個Service實例,依次調用其onCreate和onStartCommand方法,而後進入運行狀態,此後,若是再使用startService啓動服務時,再也不建立新的服務對象,系統會自動找到剛纔建立的Service實例,調用其onStart方法;若是咱們想要停掉一個服務,可以使用stopService方法,此時onDestroy方法會被調用,須要注意的是,無論前面使用了多個次startService,只需一次stopService,便可停掉服務。this

(2).bindService啓動模式下的生命週期:在這種模式下,當調用者首次使用bindService綁定一個服務時,系統會實例化一個Service實例,並一次調用其onCreate方法和onBind方法,而後調用者就能夠和服務進行交互了,此後,若是再次使用bindService綁定服務,系統不會建立新的Service實例,也不會再調用onBind方法;若是咱們須要解除與這個服務的綁定,可以使用unbindService方法,此時onUnbind方法和onDestroy方法會被調用。spa

兩種模式有如下幾點不一樣之處:startService模式下調用者與服務無必然聯繫,即便調用者結束了本身的生命週期,只要沒有使用stopService方法中止這個服務,服務仍會運行;一般狀況下,bindService模式下服務是與調用者生死與共的,在綁定結束以後,一旦調用者被銷燬,服務也就當即終止,就像江湖上的一句話:不求同生,希望同死。

值得一提的是,之前咱們在使用startService啓動服務時都是習慣重寫onStart方法,在Android2.0時系統引進了onStartCommand方法取代onStart方法,爲了兼容之前的程序,在onStartCommand方法中其實調用了onStart方法,不過咱們最好是重寫onStartCommand方法。

以上兩種模式的流程以下圖所示:

下面咱們就結合實例來演示一下這兩種模式的生命週期過程。咱們新建一個名爲service的項目,而後建立一個MyService的服務類,代碼以下:

 

[java]  view plain copy
 
  1. package com.scott.service;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6. import android.util.Log;  
  7.   
  8. public class MyService extends Service {  
  9.   
  10.     private static final String TAG = "MyService";  
  11.       
  12.     @Override  
  13.     public void onCreate() {  
  14.         super.onCreate();  
  15.         Log.i(TAG, "onCreate called.");  
  16.     }  
  17.       
  18.     @Override  
  19.     public int onStartCommand(Intent intent, int flags, int startId) {  
  20.         Log.i(TAG, "onStartCommand called.");  
  21.         return super.onStartCommand(intent, flags, startId);  
  22.     }  
  23.       
  24.     @Override  
  25.     public void onStart(Intent intent, int startId) {  
  26.         super.onStart(intent, startId);  
  27.         Log.i(TAG, "onStart called.");  
  28.     }  
  29.       
  30.     @Override  
  31.     public IBinder onBind(Intent intent) {  
  32.         Log.i(TAG, "onBind called.");  
  33.         return null;  
  34.     }  
  35.       
  36.     @Override  
  37.     public boolean onUnbind(Intent intent) {  
  38.         Log.i(TAG, "onUnbind called.");  
  39.         return super.onUnbind(intent);  
  40.     }  
  41.       
  42.     @Override  
  43.     public void onDestroy() {  
  44.         super.onDestroy();  
  45.         Log.i(TAG, "onDestroy called.");  
  46.     }  
  47. }  

而後再AndroidManifest.xml中配置服務信息,否則這個服務就不會生效,配置以下:

 

 

[html]  view plain copy
 
  1. <service android:name=".MyService">  
  2.             <intent-filter>  
  3.                  <action android:name="android.intent.action.MyService" />  
  4.                  <category android:name="android.intent.category.DEFAULT" />  
  5.             </intent-filter>  
  6.         </service>  

若是服務只是在本應用中使用,大能夠去掉<intent-filter>屬性。

 

服務搭建完成以後,咱們就來關注一下調用者MainActivity,它很簡單,只有兩個按鈕,一個是啓動服務,另外一個是中止服務,咱們來看一下他們的點擊事件:

 

[java]  view plain copy
 
  1. /** 
  2.  * 啓動服務 
  3.  * @param view 
  4.  */  
  5. public void start(View view) {  
  6.     Intent intent = new Intent(this, MyService.class);  
  7.     startService(intent);  
  8. }  
  9.   
  10. /** 
  11.  * 中止服務 
  12.  * @param view 
  13.  */  
  14. public void stop(View view) {  
  15.     Intent intent = new Intent(this, MyService.class);  
  16.     stopService(intent);  
  17. }  

接下來咱們就先點擊一次啓動按鈕,看看都發生了些什麼。日誌打印結果以下:

 

固然咱們以爲還不過癮,再點擊一次,咱們會發現結果略有不一樣:

咱們看到第二次點擊時onCreate方法就再也不被調用了,而是直接調用了onStartCommand方法(onStartCommand中又調用了onStart方法)。咱們選擇「Settings->Application s->Running services」就會發現咱們剛剛啓動的服務:

而後咱們點擊中止按鈕,試圖中止服務,咱們發現以下現象:

咱們會發現onDestroy方法被調用了,此時服務就中止運行了。咱們再次查看「Running services」,就會發現MyService這個服務已全無蹤影。

在這個過程當中,onBind方法和onUnbind方法始終沒被調用,咱們下面就讓這兩位show一下本身。

咱們修改一下MainActivity的代碼,使其能夠能夠以bindService的方式啓動一個服務,代碼以下:

 

[java]  view plain copy
 
  1. private ServiceConnection conn = new ServiceConnection() {  
  2.       
  3.     @Override  
  4.     public void onServiceConnected(ComponentName name, IBinder service) {  
  5.         //connected  
  6.         Log.i(TAG, "onServiceConnected called.");  
  7.     }  
  8.   
  9.     /** 
  10.      *  Called when a connection to the Service has been lost. 
  11.      *  This typically happens when the process hosting the service has crashed or been killed. 
  12.      *  This does not remove the ServiceConnection itself. 
  13.      *  this binding to the service will remain active, 
  14.      *  and you will receive a call to onServiceConnected when the Service is next running. 
  15.      */  
  16.     @Override  
  17.     public void onServiceDisconnected(ComponentName name) {  
  18.     }  
  19. };  
  20.   
  21. /** 
  22.  * 綁定服務 
  23.  * @param view 
  24.  */  
  25. public void bind(View view) {  
  26.     Intent intent = new Intent(this, MyService.class);  
  27.     bindService(intent, conn, Context.BIND_AUTO_CREATE);  
  28. }  
  29.   
  30. /** 
  31.  * 解除綁定 
  32.  * @param view 
  33.  */  
  34. public void unbind(View view) {  
  35.     unbindService(conn);  
  36. }  

在使用bindService綁定服務時,咱們須要一個ServiceConnection表明與服務的鏈接,它只有兩個方法,onServiceConnected和onServiceDisconnected,前者是在操做者在鏈接一個服務成功時被調用,然後者是在服務崩潰或被殺死致使的鏈接中斷時被調用,而若是咱們本身解除綁定時則不會被調用,因此咱們這裏只研究onServiceConnected這個方法。

 

看樣子是能夠去綁定一個服務了,其實還不行,由於咱們前面服務中的onBind方法返回值爲null,這樣是不行的,要想實現綁定操做,必須返回一個實現了IBinder接口類型的實例,該接口描述了與遠程對象進行交互的抽象協議,有了它咱們才能與服務進行交互。咱們因而有了這樣的代碼:

 

[java]  view plain copy
 
  1. @Override  
  2. public IBinder onBind(Intent intent) {  
  3.     Log.i(TAG, "onBind called.");  
  4.     return new Binder() {};  
  5. }  

咱們返回了一個Binder的實例,而這個Binder偏偏是實現了IBinder接口,因此這樣就能夠實現綁定服務的操做了,一塊兒來演示一下。

 

先點擊一下綁定按鈕,咱們會發如今MainActivity中打印日誌以下:

似的,onServiceConnected方法被調用了,看來綁定鏈接已經成功了,看看MyService如何:

onCreate方法和onBind方法被調用了,此時服務已進入運行階段,若是再次點擊綁定按鈕,onCreate和onBinder並不會再次被調用,這個過程當中它們僅被調用一次。

而後點擊解除綁定按鈕,咱們會發現MyService打印以下:

能夠看到onUnbind方法和onDestroy方法被調用了,此時MyService已被銷燬,整個生命週期結束。

另外一方面,當咱們退出MainActivity時,服務也會隨之而結束,從這一點上看,MyService能夠說是誓死追隨着MainActivity。

須要注意的是,在鏈接中斷狀態再去作解除綁定操做會引發一個異常,在MainActivity銷燬以前沒有進行解除綁定也會致使後臺出現異常信息,此時咱們就要想辦法確保不會出現此類狀況,能夠這樣作:

 

[java]  view plain copy
 
  1. private boolean binded;  
  2.   
  3. @Override  
  4. public void onServiceConnected(ComponentName name, IBinder service) {  
  5.     binded = true;  
  6. }  
  7.   
  8. /** 
  9.  * 解除綁定 
  10.  * @param view 
  11.  */  
  12. public void unbind(View view) {  
  13.     unbindService();  
  14. }  
  15.   
  16. @Override  
  17. protected void onDestroy() {  
  18.     super.onDestroy();  
  19.     unbindService();  
  20. }  
  21.   
  22. /** 
  23.  * 解除服務綁定 
  24.  */  
  25. private void unbindService() {  
  26.     if (binded) {  
  27.         unbindService(conn);  
  28.         binded = false;  
  29.     }  
  30. }  

以上就是bindService的生命週期,正如咱們上面講的同樣,使用bindService啓動服務後調用者和服務綁定到了一塊兒,當調用者被銷燬,服務也當即結終止。

一般狀況下是這樣的,不過也有特殊狀況。當startService和bindService在同一場合下使用時,就會出現稍微不一樣的現象。

若是咱們先以startService方式啓動服務,而後再用bindService綁定到這個服務,以後使用unbindService解除綁定,此時服務並不會所以而終止,而是繼續運行,直到咱們使用stopService來中止這個服務。下面咱們再修改一下代碼以驗證這個過程。MyService保持不變,咱們只需修改一下MainActivity。MainActivity最新代碼以下:

 

[java]  view plain copy
 
  1. package com.scott.service;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.IBinder;  
  10. import android.util.Log;  
  11. import android.view.View;  
  12.   
  13. public class MainActivity extends Activity {  
  14.       
  15.     private static final String TAG = "MainActivity";  
  16.       
  17.     @Override  
  18.     public void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.main);  
  21.     }  
  22.       
  23.     private ServiceConnection conn = new ServiceConnection() {  
  24.           
  25.         @Override  
  26.         public void onServiceConnected(ComponentName name, IBinder service) {  
  27.             Log.i(TAG, "onServiceConnected called.");  
  28.         }  
  29.           
  30.         @Override  
  31.         public void onServiceDisconnected(ComponentName name) {  
  32.         }  
  33.     };  
  34.       
  35.     /** 
  36.      * 啓動服務 
  37.      * @param view 
  38.      */  
  39.     public void start(View view) {  
  40.         Intent intent = new Intent(this, MyService.class);  
  41.         startService(intent);  
  42.     }  
  43.       
  44.     /** 
  45.      * 綁定服務 
  46.      * @param view 
  47.      */  
  48.     public void bind(View view) {  
  49.         Intent intent = new Intent(this, MyService.class);  
  50.         bindService(intent, conn, Context.BIND_AUTO_CREATE);  
  51.     }  
  52.       
  53.     /** 
  54.      * 解除綁定 
  55.      * @param view 
  56.      */  
  57.     public void unbind(View view) {  
  58.         unbindService(conn);  
  59.     }  
  60.       
  61.     /** 
  62.      * 中止服務 
  63.      * @param view 
  64.      */  
  65.     public void stop(View view) {  
  66.         Intent intent = new Intent(this, MyService.class);  
  67.         stopService(intent);  
  68.     }  
  69. }  

在MainActivity中包含了四個按鈕事件,分別是startService、bindService、unbindService和stopService,咱們逐一地按下,看看都發生了什麼。

 

首先按下啓動服務的按鈕,MyService打印以下:

恩,意料之中。而後咱們再按下綁定服務的按鈕,MyService打印以下:

此時,只有onBind被調用,以後二者就綁定成功。咱們再按下解除綁定的按鈕,MyService打印以下:

此時,onUnbind方法方法被調用,注意,此時MyService並無因解除綁定而終止,而是繼續運行。也許咱們內心會問,若是屢次按下綁定服務的按鈕或重複以上兩個步驟,結果如何呢?答案是onBind和onUnbind都不會再被調用了。看不到onBind被調用,是否是沒有綁定成功啊,咱們來看一下MainActivity打印信息:

重複按下綁定按鈕,幾回都綁定成功了。最後咱們按下中止服務的按鈕,MyService打印以下:

此時,onDestroy被調用了,此時MyService中止了運行,整個生命週期結束。

以上就是關於MyService生命週期的講解,下面咱們來介紹一下如何與服務進行通訊。與服務之間的通訊能夠分爲兩種,進程內的通訊和進程間的通訊,前者調用者和服務在同一應用進程內,然後者是分佈在不一樣應用進程中的。

2.進程內與服務通訊

進程內與服務通訊實際上就是經過bindService的方式與服務綁定,獲取到通訊中介Binder實例,而後經過調用這個實例的方法,完成對服務的各類操做。咱們上面也介紹了很多關於bindService的內容,下面咱們就針對實際需求對代碼作改動。首先是MyService,代碼以下:

 

[java]  view plain copy
 
  1. package com.scott.service;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.Binder;  
  6. import android.os.IBinder;  
  7. import android.util.Log;  
  8.   
  9. public class MyService extends Service {  
  10.   
  11.     private static final String TAG = "MyService";  
  12.       
  13.     @Override  
  14.     public IBinder onBind(Intent intent) {  
  15.         Log.i(TAG, "onBind called.");  
  16.         return new MyBinder();  
  17.     }  
  18.       
  19.     /** 
  20.      * 綁定對象 
  21.      * @author user 
  22.      * 
  23.      */  
  24.     public class MyBinder extends Binder {  
  25.           
  26.         /** 
  27.          * 問候 
  28.          * @param name 
  29.          */  
  30.         public void greet(String name) {  
  31.             Log.i(TAG, "hello, " + name);  
  32.         }  
  33.     }  
  34. }  

咱們建立了一個MyBinder的內部類,定義了一個greet方法,在onBind方法中就將這個MyBinder的實例返回,只要調用者獲取到這個實例,就能夠像拿着遊戲手柄同樣對服務進行操做。咱們來看一下調用者的代碼吧,MainActivity代碼以下:

 

 

[java]  view plain copy
 
  1. package com.scott.service;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.IBinder;  
  10. import android.view.View;  
  11.   
  12. public class MainActivity extends Activity {  
  13.       
  14.     /** 
  15.      * 綁定對象實例 
  16.      */  
  17.     private MyService.MyBinder binder;  
  18.       
  19.     @Override  
  20.     public void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.main);  
  23.     }  
  24.       
  25.     private ServiceConnection conn = new ServiceConnection() {  
  26.           
  27.         @Override  
  28.         public void onServiceConnected(ComponentName name, IBinder service) {  
  29.             binder = (MyService.MyBinder) service;  //獲取其實例  
  30.             binder.greet("scott");                  //調用其方法  
  31.         }  
  32.           
  33.         @Override  
  34.         public void onServiceDisconnected(ComponentName name) {  
  35.         }  
  36.     };  
  37.       
  38.     /** 
  39.      * 綁定服務 
  40.      * @param view 
  41.      */  
  42.     public void bind(View view) {  
  43.         Intent intent = new Intent(this, MyService.class);  
  44.         bindService(intent, conn, Context.BIND_AUTO_CREATE);  
  45.     }  
  46.       
  47.     /** 
  48.      * 解除綁定 
  49.      * @param view 
  50.      */  
  51.     public void unbind(View view) {  
  52.         unbindService(conn);  
  53.     }  
  54. }  

在上面的代碼中,咱們是在綁定服務成功時將IBinder類型的service參數強轉爲MyService.MyBinder類型,獲取綁定中介實例,而後調用其greet方法。

 

操做一下,看看效果如何。先點擊綁定服務的按鈕,MyService打印以下:

須要注意的是,與服務綁定是一個異步的過程,也就是說,在這一刻咱們綁定服務,下一刻咱們去操做binder對象,也許它還爲null,這就容易引發空指針異常,正確的作法是把這些操做放到綁定成功以後,確保萬無一失。

以上就是進程內通訊的內容。

相關文章
相關標籤/搜索