Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發之低調的Service

Android開發之低調的Service

編輯:關於Android編程

锲而捨之,朽木不折;锲而不捨,金石可镂。——荀況

今天學習了一下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。

 

 

 



 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved