Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> [Android] Service全面總結

[Android] Service全面總結

編輯:關於Android編程

什麼是服務?  

  Service是一個應用程序組件,它能夠在後台執行一些耗時較長的操作,並且不提供用戶界面。服務能被其它應用程序的組件啟動,即使用戶切換到另外的應用時還能保持後台運行。此外,應用程序組件還能與服務綁定,並與服務進行交互,甚至能進行進程間通信(IPC)。 比如,服務可以處理網絡傳輸、音樂播放、執行文件I/O、或者與content provider進行交互,所有這些都是後台進行的。

Service 與 Thread 的區別

  服務僅僅是一個組件,即使用戶不再與你的應用程序發生交互,它仍然能在後台運行。因此,應該只在需要時才創建一個服務。

  如果你需要在主線程之外執行一些工作,但僅當用戶與你的應用程序交互時才會用到,那你應該創建一個新的線程而不是創建服務。 比如,如果你需要播放一些音樂,但只是當你的activity在運行時才需要播放,你可以在onCreate()中創建一個線程,在onStart()中開始運行,然後在onStop()中終止運行。還可以考慮使用AsyncTask或HandlerThread來取代傳統的Thread類。

  由於無法在不同的 Activity 中對同一 Thread 進行控制,這個時候就要考慮用服務實現。如果你使用了服務,它默認就運行於應用程序的主線程中。因此,如果服務執行密集計算或者阻塞操作,你仍然應該在服務中創建一個新的線程來完成(避免ANR)。

服務的分類

按運行分類

前台服務

  前台服務是指那些經常會被用戶關注的服務,因此內存過低時它不會成為被殺的對象。 前台服務必須提供一個狀態欄通知,並會置於“正在進行的”(“Ongoing”)組之下。這意味著只有在服務被終止或從前台移除之後,此通知才能被解除。
  例如,用服務來播放音樂的播放器就應該運行在前台,因為用戶會清楚地知曉它的運行情況。 狀態欄通知可能會標明當前播放的歌曲,並允許用戶啟動一個activity來與播放器進行交互。

  要把你的服務請求為前台運行,可以調用startForeground()方法。此方法有兩個參數:唯一標識通知的整數值、狀態欄通知Notification對象。例如:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),System.currentTimeMillis());

Intent notificationIntent = new Intent(this,ExampleActivity.class);

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);

startForeground(ONGOING_NOTIFICATION, notification);

  要從前台移除服務,請調用stopForeground()方法,這個方法接受個布爾參數,表示是否同時移除狀態欄通知。此方法不會終止服務。不過,如果服務在前台運行時被你終止了,那麼通知也會同時被移除。

後台服務

按使用分類  

本地服務

  用於應用程序內部,實現一些耗時任務,並不占用應用程序比如Activity所屬線程,而是單開線程後台執行。
  調用Context.startService()啟動,調用Context.stopService()結束。在內部可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。

遠程服務

  用於Android系統內部的應用程序之間,可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可。可以定義接口並把接口暴露出來,以便其他應用進行操作。客戶端建立到服務對象的連接,並通過那個連接來調用服務。調用Context.bindService()方法建立連接,並啟動,以調用 Context.unbindService()關閉連接。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。

Service生命周期

  這裡寫圖片描述

Service生命周期方法:

public class ExampleService extends Service {
    int mStartMode;       // 標識服務被殺死後的處理方式
    IBinder mBinder;      // 用於客戶端綁定的接口
    boolean mAllowRebind; // 標識是否使用onRebind

    @Override
    public void onCreate() {
        // 服務正被創建
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 服務正在啟動,由startService()調用引發
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // 客戶端用bindService()綁定服務
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // 所有的客戶端都用unbindService()解除了綁定
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // 某客戶端正用bindService()綁定到服務,
        // 而onUnbind()已經被調用過了
    }
    @Override
    public void onDestroy() {
        // 服務用不上了,將被銷毀
    }
}

請注意onStartCommand()方法必須返回一個整數。這個整數是描述系統在殺死服務之後應該如何繼續運行。onStartCommand()的返回值必須是以下常量之一:

START_NOT_STICKY  
如果系統在onStartCommand()返回後殺死了服務,則不會重建服務了,除非還存在未發送的intent。 當服務不再是必需的,並且應用程序能夠簡單地重啟那些未完成的工作時,這是避免服務運行的最安全的選項。 
 
START_STICKY
如果系統在onStartCommand()返回後殺死了服務,則將重建服務並調用onStartCommand(),但不會再次送入上一個intent, 而是用null intent來調用onStartCommand() 。除非還有啟動服務的intent未發送完,那麼這些剩下的intent會繼續發送。 這適用於媒體播放器(或類似服務),它們不執行命令,但需要一直運行並隨時待命。 

START_REDELIVER_INTENT
如果系統在onStartCommand()返回後殺死了服務,則將重建服務並用上一個已送過的intent調用onStartCommand()。任何未發送完的intent也都會依次送入。這適用於那些需要立即恢復工作的活躍服務,比如下載文件。

  服務的生命周期與activity的非常類似。不過,更重要的是你需密切關注服務的創建和銷毀環節,因為後台運行的服務是不會引起用戶注意的。

  服務的生命周期——從創建到銷毀——可以有兩種路徑:

一個started服務

  這類服務由其它組件調用startService()來創建。然後保持運行,且必須通過調用stopSelf()自行終止。其它組件也可通過調用stopService() 終止這類服務。服務終止後,系統會把它銷毀。

  如果一個Service被startService 方法多次啟動,那麼onCreate方法只會調用一次,onStart將會被調用多次(對應調用startService的次數),並且系統只會創建Service的一個實例(因此你應該知道只需要一次stopService調用)。該Service將會一直在後台運行,而不管對應程序的Activity是否在運行,直到被調用stopService,或自身的stopSelf方法。當然如果系統資源不足,android系統也可能結束服務。

 一個bound服務

  服務由其它組件(客戶端)調用bindService()來創建。然後客戶端通過一個IBinder接口與服務進行通信。客戶端可以通過調用unbindService()來關閉聯接。多個客戶端可以綁定到同一個服務上,當所有的客戶端都解除綁定後,系統會銷毀服務。(服務不需要自行終止。)

如果一個Service被某個Activity 調用 Context.bindService 方法綁定啟動,不管調用 bindService 調用幾次,onCreate方法都只會調用一次,同時onStart方法始終不會被調用。當連接建立之後,Service將會一直運行,除非調用Context.unbindService 斷開連接或者之前調用bindService 的 Context 不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被調用。

 

這裡寫圖片描述

 

這兩條路徑並不是完全隔離的。也就是說,你可以綁定到一個已經用startService()啟動的服務上。例如,一個後台音樂服務可以通過調用startService()來啟動,傳入一個指明所需播放音樂的 Intent。 之後,用戶也許需要用播放器進行一些控制,或者需要查看當前歌曲的信息,這時一個activity可以通過調用bindService()與此服務綁定。在類似這種情況下,stopService()或stopSelf()不會真的終止服務,除非所有的客戶端都解除了綁定。

  當在旋轉手機屏幕的時候,當手機屏幕在“橫”“豎”變換時,此時如果你的 Activity 如果會自動旋轉的話,旋轉其實是 Activity 的重新創建,因此旋轉之前的使用 bindService 建立的連接便會斷開(Context 不存在了)。

在manifest中聲明服務

  無論是什麼類型的服務都必須在manifest中申明,

Service 元素的屬性有:

android:name  ————-  服務類名

android:label  ————–  服務的名字,如果此項不設置,那麼默認顯示的服務名則為類名

android:icon  ————–  服務的圖標

android:permission  ——-  申明此服務的權限,這意味著只有提供了該權限的應用才能控制或連接此服務

android:process  ———-  表示該服務是否運行在另外一個進程,如果設置了此項,那麼將會在包名後面加上這段字符串表示另一進程的名字

android:enabled  ———-  如果此項設置為 true,那麼 Service 將會默認被系統啟動,不設置默認此項為 false

android:exported  ———  表示該服務是否能夠被其他應用程序所控制或連接,不設置默認此項為 false 

  android:name是唯一必需的屬性——它定義了服務的類名。與activity一樣,服務可以定義intent過濾器,使得其它組件能用隱式intent來調用服務。如果你想讓服務只能內部使用(其它應用程序無法調用),那麼就不必(也不應該)提供任何intent過濾器。
  此外,如果包含了android:exported屬性並且設置為”false”, 就可以確保該服務是你應用程序的私有服務。即使服務提供了intent過濾器,本屬性依然生效。 

startService 啟動服務

  從activity或其它應用程序組件中可以啟動一個服務,調用startService()並傳入一個Intent(指定所需啟動的服務)即可。

    Intent intent = new Intent(this, MyService.class);
    startService(intent);

服務類:

public class MyService extends Service {

      /**
     * onBind 是 Service 的虛方法,因此我們不得不實現它。
     * 返回 null,表示客服端不能建立到此服務的連接。
     */
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
 public int onStartCommand(Intent intent, int flags, int startId)     {     
    //接受傳遞過來的intent的數據 
     return START_STICKY; 
    };

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

}

  一個started服務必須自行管理生命周期。也就是說,系統不會終止或銷毀這類服務,除非必須恢復系統內存並且服務返回後一直維持運行。 因此,服務必須通過調用stopSelf()自行終止,或者其它組件可通過調用stopService()來終止它。

bindService 啟動服務  

  當應用程序中的activity或其它組件需要與服務進行交互,或者應用程序的某些功能需要暴露給其它應用程序時,你應該創建一個bound服務,並通過進程間通信(IPC)來完成。

方法如下:

 Intent intent=new Intent(this,BindService.class); 
 bindService(intent, ServiceConnection conn, int flags)  

注意bindService是Context中的方法,當沒有Context時傳入即可。

在進行服務綁定的時,其flags有:

Context.BIND_AUTO_CREATE

  表示收到綁定請求的時候,如果服務尚未創建,則即刻創建,在系統內存不足需要先摧毀優先級組件來釋放內存,且只有駐留該服務的進程成為被摧毀對象時,服務才被摧毀 

Context.BIND_DEBUG_UNBIND  

  通常用於調試場景中判斷綁定的服務是否正確,但容易引起內存洩漏,因此非調試目的的時候不建議使用

Context.BIND_NOT_FOREGROUND  

  表示系統將阻止駐留該服務的進程具有前台優先級,僅在後台運行。

服務類:

public class BindService extends Service {

     // 實例化MyBinder得到mybinder對象;
    private final MyBinder binder = new MyBinder();

      /**
     * 返回Binder對象。
     */
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return binder;
    }

     /**
      * 新建內部類MyBinder,繼承自Binder(Binder實現IBinder接口),
      * MyBinder提供方法返回BindService實例。
      */
  public class MyBinder extends Binder{

        public BindService getService(){
            return BindService.this;
        }
    }


    @Override
    public boolean onUnbind(Intent intent) {
        // TODO Auto-generated method stub
        return super.onUnbind(intent);
    }
}

啟動服務的activity代碼:

public class MainActivity extends Activity {

    /** 是否綁定 */  
    boolean mIsBound = false; 
    BindService mBoundService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        doBindService();
    }
    /**
     * 實例化ServiceConnection接口的實現類,用於監聽服務的狀態
     */
    private ServiceConnection conn = new ServiceConnection() {  

        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            BindService mBoundService = ((BindService.MyBinder) service).getService();  

        }  

        @Override  
        public void onServiceDisconnected(ComponentName name) {  
            mBoundService = null;  

        }  
    }; 

    /** 綁定服務 */  
    public void doBindService() {  
        bindService(new Intent(MainActivity.this, BindService.class), conn,Context.BIND_AUTO_CREATE);  
        mIsBound = true;  
    }  

    /** 解除綁定服務 */  
    public void doUnbindService() {  
        if (mIsBound) {  
            // Detach our existing connection.  
            unbindService(conn);  
            mIsBound = false;  
        }  
    } 

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();

        doUnbindService();
    }
}

注意在AndroidMainfest.xml中對Service進行顯式聲明

判斷Service是否正在運行:

private boolean isServiceRunning() {
    ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);

  {
        if ("com.example.demo.BindService".equals(service.service.getClassName()))   {
            return true;
        }
    }
    return false;
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved