Android開發之低調的Service

來源:互聯網
上載者:User

Android開發之低調的Service

鍥而舍之,朽木不折;鍥而不捨,金石可鏤。——荀況

今天學習了一下Service的用法就和大家一起來討論Android中Service的相關知識點,如有謬誤,歡迎批評指正,如有疑問歡迎留言。

一、Service用途

Service在Android中和Activity是屬於同一層級上的組件,Android中的Service,其意思是“服務”,它在後台運行不可互動。Service自己不能運行,需要通過某一個Activity或者其它Context對象來調用,Context.startService()和Context.bindService()兩種方式啟動 Service 。 Service在Android中和Activity是屬於同一層級上的組件,Android 中的Service ,其意思是“服務”,它是在後台運行,不可互動的。Service自己不能運行,需要通過某一個Activity或者其它Context對象來調用,Context.startService()和Context.bindService()兩種方式啟動 Service 。
Android 中的服務,它與 Activity不同,它是不能與使用者互動的,不能自己啟動的,運行在背景程式(乾著重的工作,卻連個介面也沒有,因此我說它低調),如果我們退出應用時, Service進程並沒有結束,它仍然在後台運行,那我們什麼時候會用到Service呢?比如我們播放音樂的時候,有可能想邊聽音樂邊幹些其他事情,當我們退出播放音樂的應用,如果不用 Service,我們就聽不到歌了,所以這時候就得用到Service了,又比如當我們一個應用的資料是通過網路擷取的,不同時間(一段時間)的資料是不同的,這時候我們可以用 Service在後台定時更新,而不用每開啟應用的時候在去擷取。如果在 Service的 onCreate或者 onStart方法中做一些很耗時的動作,最好是啟動一個新線程來運行這個 Service,因為,如果 Service運行在主線程中,會影響到程式的 UI操作或者阻塞主線程中的其它事情。

二、Service的生命週期

首先來看官網給出的Service的生命週期圖

 

1.onCreate() 建立Service
2.onStart(Intent intent, int startId) 啟動Service 3.onStartCommand(Intent intent, int flags, int startId)啟動Service
4.onDestroy() 銷毀Service
5.onBind() 返回一個IBinder介面對象給Service
從這個生命週期圖中我們可以看到有兩種啟動Service的方法Context.startService和Context.bindService,對應的生命週期也分為兩種情況: (1)startService啟動模式 可以看出當我們採用Context.startService(intent)這種方式時,系統會執行個體化一個服務,依次調用onCreate()和onStartCommand()方法,之後服務就進入了運行狀態,如果服務已經運行了我們再次啟動時不會重新建立服務,系統會自動調用剛才我們啟動的服務,並調用其onStart()方法,如果我們想銷毀一個服務可以使用stopService(intent)方法,使用stopService()方法會調用onDestroy()方法此時服務就被銷毀了,
(2)bindService啟動模式 在這種模式下,調用bindService(Intent service, ServiceConnection conn, int flags)來綁定一個Service,這時Service會調用自身的onCreate()方法(前提是該Service未建立),系統會執行個體化一個服務,接著調用onBind(intent)方法調用onBind方法後調用者就可以和服務進行互動了,當我們採用bindService這種方法建立服務時,如果已經建立好了一個如果再進行建立,系統不會建立新的Service執行個體,也不會調用onBind方法,這種方式啟動的服務的銷毀方法是使用unbindService方法,此時onUnbind方法和onDestroy方法都會被調用。 關於bindService(Intent service, ServiceConnection conn, int flags)參數的說明 參數①service: Intent 對象 參數②conn: ServiceConnection對象,實現其onServiceConnected()和onServiceDisconnected()在串連成功和中斷連線時處理。後面有執行個體來進行說明 參數③flags:Service建立的方式,一般用Service.BIND_AUTO_CREATE表示綁定時自動建立。
可能有的人會問這兩種啟動模式有什麼不同? strarService和bindService的不同之處:startService模式下調用者與服務無必然聯絡,即使調用者結束了自己的生命週期,只要沒有使用stopService方法停止這個服務,服務仍會運行;然而通常情況下,bindService模式下服務是與調用者同生共死的,在綁定結束之後,一旦調用者被銷毀,服務就會終止,我們通常用一句話來形容bindService:不求同生,但求同死。
另外需要注意的是在Android2.0之前我們使用startService啟動服務時都是習慣重寫onStart方法,在Android2.0時系統引進了onStartCommand方法取代了onStart方法,但是為了相容以前的程式,在onStartCommand方法中其實是調用了onStart方法,我們之後會做驗證,不過我們最好還是重寫onStartCommand方法。
小平同志曾經說過,實踐是檢驗真理的唯一標準,下面我們就結合執行個體對上面的理論來做驗證,以便加深印象 布局如下:

 

 

代碼如下: 1.Service的代碼
package com.example.servicepractice;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;public class MyService extends Service {          private static final String TAG = MyService;     @Override     public void onCreate() {            super.onCreate();           Log. i(TAG,onCreate called );     }          @Override     public void onStart(Intent intent, int startId) {            super. onStart(intent, startId);           Log. i(TAG,onStart called );     }          @Override     public int onStartCommand(Intent intent, int flags, int startId) {           Log. i(TAG,onStartCommand called );            return super.onStartCommand(intent, flags, startId);     }          @Override     public void onDestroy() {            super.onDestroy();           Log. i(TAG,onDestroy called );     }          @Override     public IBinder onBind(Intent intent) {           Log. i(TAG,onBind called );            return null;     }          @Override     public boolean onUnbind(Intent intent) {           Log. i(TAG,onUnbind called );            return super.onUnbind(intent);     }}
2.MainActivity的代碼
package com.example.servicepractice;import android.os.Bundle;import android.os.IBinder;import android.app.Activity;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.util.Log;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup.LayoutParams;import android.widget.Button;public class MainActivity extends Activity {          protected static final String TAG = MyService;          private Button btn_start;     private Button btn_stop;     private Button btn_bind;     private Button btn_unbind;     @Override     protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);           setContentView(R.layout. activity_main);                findViews();                      setClick();     }     private void findViews() {            btn_start=(Button) findViewById(R.id. btn_start);            btn_stop=(Button) findViewById(R.id. btn_stop);                       btn_bind=(Button) findViewById(R.id. btn_bind);            btn_unbind=(Button) findViewById(R.id. btn_unbind);                }          private void setClick() {            //採用startService啟動服務            btn_start.setOnClickListener( new OnClickListener() {                                 public void onClick(View v) {                     Intent intent= new Intent(MainActivity.this,MyService.class );                     startService(intent);                }           });            //銷毀服務            btn_stop.setOnClickListener( new OnClickListener() {                                 public void onClick(View v) {                     Intent intent= new Intent(MainActivity.this,MyService.class );                     stopService(intent);                }           });            //綁定服務            btn_bind.setOnClickListener( new OnClickListener() {                                 public void onClick(View v) {                     Intent intent = new Intent(MainActivity.this,MyService.class );                     bindService(intent, conn,Context. BIND_AUTO_CREATE);                }           });            //解除綁定            btn_unbind.setOnClickListener( new OnClickListener() {                                 public void onClick(View v) {                     unbindService( conn);                }           });     }       private ServiceConnection conn=new ServiceConnection() {             public void onServiceConnected(ComponentName name, IBinder service) {                 //connected                Log. i(TAG,onServiceConnection called. );           }          public void onServiceDisconnected(ComponentName name) {                           }  };   }

 

因為服務是四大組件之一所以我們要在資訊清單檔中配置,注意:如果啟動的服務沒有在資訊清單檔中配置並不會報錯(這種錯誤很難發現,切記要配置),只是啟動時不啟動這個服務,所以在開發時一定要注意養成一個好的習慣--->對於四大組件一定要聲明好之後就去配置否則如果忘記了還得花好長時間去尋找錯誤 配置代碼如下:

 

                                                         

 

如果我們的服務只在本應用中調用可以去掉這個屬性是其他應用調用本應用中的服務時所配置的屬性 從布局中我們可以看到一共有四個按鈕我們首先來看前面兩個按鈕 1、啟動服務和銷毀服務學習
            //採用startService啟動服務            btn_start.setOnClickListener( new OnClickListener() {                                 public void onClick(View v) {                     Intent intent= new Intent(MainActivity.this,MyService.class );                     startService(intent);                }           });            //銷毀服務            btn_stop.setOnClickListener( new OnClickListener() {                                 public void onClick(View v) {                      Intent intent=new Intent(MainActivity.this,MyService. class);                     stopService(intent);                }           });
首先我們點擊啟動按鈕列印日誌如下

 

我們發現它調用的方法的順序是onCreate->onStartCommand->onStart 然後我們接著點擊啟動服務按鈕列印結果如下

 

我們發現再次啟動時系統並沒有重新執行個體化這個Service,因為系統發現這個服務已經啟動了,此時它會直接調用onStartCommand方法,在onStartCommand方法中會調用onStart方法

onStartCommand方法的源碼:

     /* @param intent The Intent supplied to {@link android.content.Context#startService},     * as given.  This may be null if the service is being restarted after     * its process has gone away, and it had previously returned anything     * except {@link #START_STICKY_COMPATIBILITY}.     * @param flags Additional data about this start request.  Currently either     * 0, {@link #START_FLAG_REDELIVERY}, or {@link #START_FLAG_RETRY}.     * @param startId A unique integer representing this specific request to     * start.  Use with {@link #stopSelfResult(int)}.     *     * @return The return value indicates what semantics the system should     * use for the service's current started state.  It may be one of the     * constants associated with the {@link #START_CONTINUATION_MASK} bits.     *     * @see #stopSelfResult(int)     */    public int onStartCommand(Intent intent, int flags, int startId) {        onStart(intent , startId);        return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;    }
操作完啟動服務按鈕後我們接著點擊銷毀服務按鈕列印日誌如下

 

發現當調用 stopService(intent)這個方法是會調用Service的onDestroy方法從而銷毀服務。

2.綁定服務和解除綁定的學習

          //綁定服務            btn_bind.setOnClickListener( new OnClickListener() {                                 public void onClick(View v) {                     Intent intent = new Intent(MainActivity.this,MyService.class );                     bindService(intent, conn,Context. BIND_AUTO_CREATE);                }           });            //解除綁定            btn_unbind.setOnClickListener( new OnClickListener() {                                 public void onClick(View v) {                     unbindService( conn);                }           });
通過上面的 bindService(intent,conn,Context. BIND_AUTO_CREATE)這個方法我們發現通過需要一個ServiceConnection對象,ServiceConnection代表與服務的串連,它只有兩個方法,onServiceConnected和onServiceDisconnected,前者是在操作者在串連一個服務成功時被調用,而後者是在服務崩潰或被殺死導致的串連中斷時被調用,而如果我們自己解除綁定時則不會被調用,所以我們這裡只研究onServiceConnected這個方法。
我們首先來看下ServiceConnection的代碼
  private ServiceConnection conn= new ServiceConnection() {     /**     * Called when a connection to the Service has been established, with     * the {@link android.os.IBinder} of the communication channel to the     * Service.     *     * @param name The concrete component name of the service that has     * been connected.     *     * @param service The IBinder of the Service's communication channel,     * which you can now make calls on.     */       public void onServiceConnected(ComponentName name, IBinder service) {                 //connected                Log. i( TAG,onServiceConnection called. );           }          /**     * Called when a connection to the Service has been lost.  This typically     * happens when the process hosting the service has crashed or been killed.     * This does not  remove the ServiceConnection itself -- this     * binding to the service will remain active, and you will receive a call     * to {@link #onServiceConnected} when the Service is next running.     *     * @param name The concrete component name of the service whose     * connection has been lost.     */      public void onServiceDisconnected(ComponentName name) {                }};
到這我們還差一步就可以綁定服務了,因為在前面服務中的onBind方法傳回值為null,這樣是不行的,要想實現綁定操作,必須返回一個實現了IBinder介面類型的執行個體,該介面描述了與遠程對象進行互動的抽象協議,有了它我們才能與服務進行互動。所以我們要修改代碼
     @Override     public IBinder onBind(Intent intent) {           Log. i(TAG,onBind called );            return new Binder(){};     }
上面的代碼中返回了一個Binder的執行個體,而這個Binder恰恰是實現了IBinder介面,所以這樣就可以實現綁定服務的操作了

 

首先我們點擊一下”綁定服務“按鈕列印日誌如下 我們發現onCreate方法、onBind方法和onServiceConnection方法被調用了,此時服務已經進入運行狀態,如果此時我們再次點擊”綁定服務“按鈕,這三個方法都不會被調用然後點擊”解除綁定“按鈕列印日誌如下 可以看到onUnbind方法和onDestroy方法被調用了,此時Service已經被銷毀,整個生命週期結束。 由於bindService啟動的服務是和應用綁定到一起的所以當MainActivity退出程式時,服務也會隨之結束。 下面我們來說一種情況當我們點擊綁定服務按鈕之後我們點擊兩次“解除綁定”按鈕會發現程式崩潰了,日誌如下 還有一種情況,當我們點擊“綁定服務”按鈕後此時服務已經運行了,此時我們直接按“返回”鍵程式退出了,我們看到日誌會報一個錯
我們要解決這兩種問題,我們可以定義一個變數,然後在Activity的onDestroy方法中解除綁定就ok了,也就是做如下修改
     //定義一個變數,標識這個服務是否處於綁定狀態     private boolean binded;      //定義一個綁定服務的方法     private void unbindService(){         if( binded){                unbindService( conn);                 binded= false;           }      }      //解除綁定    btn_unbind.setOnClickListener( new OnClickListener() {                     public void onClick(View v) {         unbindService();           }     });   //在onDestroy方法中調用解除綁定的方法      protected void onDestroy() {           unbindService();     };
這樣上面兩種錯誤就解決了,也體現了我們寫代碼的嚴謹性。

3.以上兩種啟動方法混合使用的學習 在上面我們討論的都是一對相匹配的啟動和銷毀方式,可能有的人會問,那我這四個按鈕混合著點擊會有什麼效果呢,接著我們來驗證一下 首先我們點擊啟動服務按鈕列印日誌如下 這個和我們前面討論的匹配沒有問題,接著我們按下綁定服務按鈕列印日誌如下
調用了onBind方法接著我們點擊解除綁定按鈕日誌如下

此時調用了onUbind方法,值得注意的是此時服務雖然解除了但是沒有終止,而是繼續運行,這時我們再次點擊綁定服務按鈕和解除服務按鈕發現onbind和onUnbind方法都不會被調用,那麼是不是沒有綁定成功呢?答案是雖然沒有調用onBind方法但是還是綁定成功了,我們可以從如下日誌驗證


但是我們怎麼銷毀掉這個服務呢?答案是:如果我們同時使用startService與bindService,Service終止需要unbindService與stopService同時調用,才能終止Service,不管startService與bindServicede的調用順序,如果先調用unbindService此時服務不會自動終止,再調用stopService之後服務才會停止,如果先調用stopService此時服務也不會終止,而再調用unbindService或者之前調用bindService的context不在了(如Activity被finish的時候)之後服務才會自動停止;

總結+特別注意:

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、startService 啟動服務想要用startService啟動服務,不管Local (本地)還是 Remote(遠程) 我們需要做的工作都是一樣簡單。當然要記得在Androidmanifest.xml 中註冊 service。

 

 

 



 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.