Android--Service之基礎


前言

  本篇博客聊一下Android下的Service組件,對於Service組件,有點類似於Windows下的服務。Service是Android四大組件中與Activity最相似的組件,它們的區別在於:Service一直在后台運行,它沒有用戶界面。一旦Service被啟動起來之后,它就與Activity一樣,也具有自己的生命周期。

  在開發過程中,對於Activity與Service的選擇標准是:如果某個程序組件需要在運行時向用戶呈現某種界面,或者該程序需要與用戶交互,就需要使用Activity,否則就應該考慮使用Service。

  本篇博客主要內容:

  1. Service概述
  2. Service清單文件配置
  3. Service開發步驟
  4. startService
  5. bindService
  6. Service的適用場景

 

Service概述

  與開發一個Activity類似,它需要繼承Service這個抽象類,並在實現類中,需要重寫一些回調方法,用於處理Service的生命周期各部分的操作。而Service也是繼承自Context,因此它也可以調用Context里定義的如getResource()、getContentResolver()等方法。

  Service中定義的生命周期方法,對Service服務的開發大部分工作就圍繞以下幾個方法進行操作:

  • void onCreate():當該Service第一次被創建后將立即回調該方法。
  • void onStartCommand(Intent intent,int flags,int startId):每次通過startService()方法啟動Service時都會被回調。
  • void onDestroy():當Service被關閉前會被回調。
  • abstract IBinder onBind(Intent intent):該方法是Service子類必須實現的方法,如果不需要通過綁定的方式啟動服務,可以返回Null。
  • boolean onUnbind(Intent intent):當Service上綁定的所有客戶端都斷開連接將回調該方法。

   通過服務的啟動方式與適用范圍,可將服務分為兩類服務:

  • start:啟動服務,當一個Android組件(如一個Activity)調用startService()的時候,啟動一個服務。服務一旦啟動,就可以一直在后台運行下去,即使這個啟動它的組件被摧毀。這樣的服務模式,通常用於執行一個操作而不需要返回結果給調用者。
  • Bound:綁定服務,當一個Android組件(如一個Activity)調用bindService()。一個綁定服務提供了一個客戶端到服務端的接口,允許組件與服務之間進行交互,這樣可以實現跨進程的通信。綁定服務的生命周期默認是跟隨它的綁定組件的,但是一個綁定服務可以綁定多個Android組件,如果這些Android組件都被銷毀,那么這個綁定服務也將被銷毀。

  雖然上面提到了服務有兩種類別,但是一個服務類所要繼承的類是一樣的,都是Service類。也就是說,一個服務,可以包含上面兩種運行方式的服務,只是與它重載的方法有關,如果重寫了onStartCommand()即支持啟動服務,如果重寫onBiind()即支持綁定服務,所以如果同時重載實現這兩個方法即可實現兩種服務。

  而對於兩種啟動方式的服務,生命周期中被回調的方法也不一樣,下圖明確說明了Service兩種情況下的生命周期:

 

 

清單文件的配置

  Service是Android四大組件之一,所以它也必須在 AndroidManifest清單文件中進行配置,否則系統將找不到這個服務。與Activity一樣,Service的配置也是在<application/>節點下,使用<service/>配置,其中android:name屬性為Service類。

  如果開發的服務需要被外部應用操作,還需要配置<intent-filter/>節點,但是如果僅本程序使用,則無需配置它也可以。

  如果這個服務強制僅本應用操作,可以配置<service/>節點的android:exported屬性為false,這樣即使配置了<intent-filter/>,外部應用也無法操作這個服務,android:exported屬性默認為true。

  清單文件配置示例:

 1     <application>
 2          <!-- 普通的服務 -->
 3         <service android:name=".Service1"></service>
 4         <!-- 可被外部應用訪問的服務 -->
 5         <service android:name=".Service2">
 6             <intent-filter >
 7                 <action android:name="com.bgxt.Service2"/>
 8             </intent-filter>
 9         </service>
10         <!-- 無法被外部應用訪問的服務 -->
11         <service android:name=".Service3"  android:exported="false">
12             <intent-filter >
13                 <action android:name="com.bgxt.Service3"/>
14             </intent-filter>
15         </service>
16     </application>

 

Service的開發步驟

  既然Service是一個與Activity類似的Android組件,所以它的開發步驟大致也為一下幾步:

  1. 開發一個服務類,需要繼承Service或者IntentService。
  2. 在AndroidManifest清單文件中注冊Service組件。
  3. 在一個Android組件中啟動這個開發的Service組件。
  4. 服務使用完成之后,需要停止這個服務。

 

啟動服務

  啟動服務必須實現Service.onStartCommond()方法。啟動服務使用startService(Intent intent)方法開啟一個服務,僅需要傳遞一個Intent對象即可,在Intent對象中指定需要啟動的服務。而使用startService()方法啟動的服務,在服務的外部,必須使用stopService()方法停止,在服務的內部可以調用stopSelf()方法停止當前服務。一旦使用startService()或者stopSelf()方法請求停止服務,系統會就會盡快銷毀這個服務。

  對於啟動服務,一旦啟動將與訪問它的組件無任何關聯,即使訪問它的組件被銷毀了,這個服務也一直運行下去,直到被銷毀!

啟動服務-示例

  下面開發一個簡單的使用startService()啟動的服務,首先開發一個服務類:

 1 package com.example.intentdemo2;
 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 StartService extends Service {
 9     private final static String TAG = "main";
10 
11     @Override
12     public IBinder onBind(Intent arg0) {
13         // 僅通過startService()啟動服務,所以這個方法返回null即可。
14         return null;
15     }
16 
17     @Override
18     public void onCreate() {
19         super.onCreate();
20         Log.i(TAG, "Service is Created");        
21     }
22 
23     @Override
24     public int onStartCommand(Intent intent, int flags, int startId) {
25         Log.i(TAG, "Service is started");
26         return super.onStartCommand(intent, flags, startId);
27     }
28 
29     @Override
30     public void onDestroy() {
31         Log.i(TAG, "Service is Destroyed");
32         super.onDestroy();
33     }
34 
35 }

  雖然這個Service類沒有什么處理邏輯,但是它包含了Service框架,在實際開發過程中,只需要在其中回調的方法中加入實際的業務實現代碼即可。下面再使用一個Activity來操作這個服務,在這個Activity中有兩個按鈕,分別用於啟動和停止這個服務。

 1 package com.example.intentdemo2;
 2 
 3 import android.app.Activity;
 4 import android.content.ComponentName;
 5 import android.content.Intent;
 6 import android.os.Bundle;
 7 import android.util.Log;
 8 import android.view.View;
 9 import android.widget.Button;
10 
11 public class ServiceActivity1 extends Activity {
12     private Button btnSer1, btnSer2;
13     private Intent service=null;
14     @Override
15     protected void onCreate(Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17         setContentView(R.layout.activity_service1);
18         btnSer1 = (Button) findViewById(R.id.btnSer1);
19         btnSer2 = (Button) findViewById(R.id.btnSer2);
20         // 設置服務啟動的Intent
21         service=new Intent(ServiceActivity1.this,StartService.class);
22         btnSer1.setOnClickListener(new View.OnClickListener() {
23 
24             @Override
25             public void onClick(View v) {
26                 // 啟動服務
27                 startService(service);
28             }
29         });
30 
31         btnSer2.setOnClickListener(new View.OnClickListener() {
32 
33             @Override
34             public void onClick(View v) {
35                 // 停止服務
36                 stopService(service);
37             }
38         });
39     }
40 }

  執行結果均在日志中,可以在LogCat中查看,先啟動服務,再停止服務:

   

綁定服務

  如果Service和宿主之間需要進行方法調用或者數據交換,則應該使用Context對象的bindService()和unbindService()方法來綁定和解除綁定服務。

  Context的bindService()方法的完整方法簽名為:

    bindService(Intent service,ServiceConnection conn,int flags)

  下面簡單介紹一下這個方法的三個參數的意義:

  • service:通過Intent指定要綁定的Service。
  • conn:一個ServiceConnection對象,該對象用於監聽訪問者與Service對象的onServiceConnected()方法。
  • flags:指定綁定時是否自動創建Service。0不自動創建、BIND_AUTO_CREATE,自動創建。

  從上面的bindService方法可以看出,綁定一個服務於宿主交互,依托於一個ServiceConnection接口,這個接口對象必須聲明在主線程中,通過實現其中的兩個方法,來實現與Service的交互,下面分別介紹一下這兩個方法:

  • void onServiceConnection(ComponentName name,IBinder service):綁定服務的時候被回調,在這個方法獲取綁定Service傳遞過來的IBinder對象,通過這個IBinder對象,實現宿主和Service的交互。
  • void onServiceDisconnected(ComponentName name):當取消綁定的時候被回調。但正常情況下是不被調用的,它的調用時機是當Service服務被意外銷毀時,例如內存的資源不足時這個方法才被自動調用。

   在使用綁定的服務的時候,該Service類必須提供一個IBinder onBind(Intent intent)方法,在綁定本地Service的情況下,onBind()方法說返回的IBinder對象會傳給宿主的ServiceConnection.onServiceConnected()方法的service參數,這樣宿主就可以通過IBinder對象與Service進行通信。實際開發中一般會繼承Binder類(IBinder的實現類)的方式實現自己的IBinder對象。

  需要注意的是,如果綁定服務提供的onBind()方法返回為Null,則也可以使用bindService()啟動服務,但不會綁定上Service,因此宿主的ServiceConnection.onServiceConnected()方法不會被執行,也就不存在於宿主與服務的交互。

綁定服務-示例

  說了這么多綁定服務相關的內容,下面通過一個例子來實現Service的綁定與數據交互。

  開發一個Service類,用於進行綁定,在Service類中,做一個簡單的數值累加,每秒加一。

 1 package com.example.intentdemo2;
 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 BindService extends Service {
10     private final static String TAG = "main";
11     private int count;
12     private boolean quit;
13     
14     private Thread thread;
15     private MyBinder binder=new MyBinder();
16     public class MyBinder extends Binder
17     {
18         // 聲明一個方法,把count暴露給外部程序。
19         public int getCount(){
20             return count;
21         }
22     }
23     
24     @Override
25     public void onCreate() {
26         super.onCreate();
27         Log.i(TAG, "Service is Created");
28         thread=new Thread(new Runnable() {            
29             @Override
30             public void run() {
31                 // 每間隔一秒count加1 ,直到quit為true。
32                 while(!quit){
33                     try{
34                         Thread.sleep(1000);
35                     }catch(InterruptedException e){
36                         e.printStackTrace();
37                     }
38                     count++;
39                 }
40             }
41         });
42         thread.start();
43     }
44     
45     @Override
46     public boolean onUnbind(Intent intent) {
47         Log.i(TAG, "Service is Unbinded");
48         return true;
49     }
50     
51     @Override
52     public int onStartCommand(Intent intent, int flags, int startId) {
53         Log.i(TAG, "Service is started");
54         return super.onStartCommand(intent, flags, startId);
55     }
56 
57     @Override
58     public void onDestroy() {
59         super.onDestroy();
60         Log.i(TAG, "Service is Destroyed");
61         this.quit=true;
62         
63     }
64     
65     @Override
66     public IBinder onBind(Intent intent) {
67         Log.i(TAG, "Service is Binded");
68         return binder;
69     }
70 }

  然后使用一個Activity來綁定上面這個Service類,並且聲明一個ServiceConnection對象,用於進行數據交互。 

 1 package com.example.intentdemo2;
 2 
 3 import android.app.Activity;
 4 import android.app.Service;
 5 import android.content.ComponentName;
 6 import android.content.Intent;
 7 
 8 import android.content.ServiceConnection;
 9 import android.os.Bundle;
10 import android.os.IBinder;
11 import android.util.Log;
12 import android.view.View;
13 import android.widget.Button;
14 import android.widget.Toast;
15 
16 public class ServiceActivity2 extends Activity {
17     private final String TAG = "main";
18     Button bind, unbind, getServiceStatus;
19     BindService.MyBinder binder;
20     private ServiceConnection conn = new ServiceConnection() {
21         @Override
22         public void onServiceDisconnected(ComponentName name) {            
23             Log.i(TAG, "--Service Disconnected--");
24         }
25         @Override
26         public void onServiceConnected(ComponentName name, IBinder service) {
27             Log.i(TAG, "--Service Connected--");
28             // 取得Service對象中的Binder對象
29             binder = (BindService.MyBinder) service;
30         }
31     };
32 
33     @Override
34     protected void onCreate(Bundle savedInstanceState) {
35         super.onCreate(savedInstanceState);
36         setContentView(R.layout.activity_bindservice1);
37         
38         bind = (Button) findViewById(R.id.bind);
39         unbind = (Button) findViewById(R.id.unbind);
40         getServiceStatus = (Button) findViewById(R.id.getServiceStatus);
41         
42         final Intent intent = new Intent();
43         // 指定開啟服務的action
44         intent.setAction("com.bgxt.BindServiceDemo");
45         
46         bind.setOnClickListener(new View.OnClickListener() {
47 
48             @Override
49             public void onClick(View v) {
50                 // 綁定服務到當前activity中
51                 bindService(intent, conn, Service.BIND_AUTO_CREATE);
52             }
53         });
54         unbind.setOnClickListener(new View.OnClickListener() {
55 
56             @Override
57             public void onClick(View v) {
58                 // 解除綁定
59                 binder=null;
60                 unbindService(conn);
61             }
62         });
63         getServiceStatus.setOnClickListener(new View.OnClickListener() {
64 
65             @Override
66             public void onClick(View v) {
67                 if(binder!=null)
68                 {
69                     // 通過綁定服務傳遞的Binder對象,獲取Service暴露出來的數據
70                     Toast.makeText(ServiceActivity2.this,
71                             "Service的Count值為" + binder.getCount(),
72                             Toast.LENGTH_SHORT).show();
73                     Log.i(TAG, "Service的Count值為" + binder.getCount());
74                 }
75                 else
76                 {
77                     Toast.makeText(ServiceActivity2.this,
78                             "還沒綁定呢,先綁定。",
79                             Toast.LENGTH_SHORT).show();
80                 }                
81             }
82         });
83     }
84 }

  執行結果,先綁定服務,然后獲取當前服務運行時count的值,最后解除綁定,把執行過程輸出到LogCat中。

 

Service的選用

  相信看完以上的內容,對Android下Service組件有了一定的了解,但是對於選用startService()還是使用bindService(),有一些原則需要講明。如果僅僅是想要啟動一個后台服務長期駐留在內存中執行某項任務,那么僅使用startService()啟動一個服務即可。但是如果想要與正在運行的服務進行交互,一種方式是使用broadcast,這個以后再介紹,另外一種方式就是使用bindService()綁定一個服務到組件上,使用broadcast的缺點是如果數據交互頻繁,容易造成性能上的問題,並且BroadcastReceiver本身執行代碼的時間不確定,也許代碼執行到一半,后面的代碼將不被執行,但是使用bindService()綁定服務即沒有這些問題。另外,如果一個服務是依托於某項應用的,那么也最好使用綁定服務,在依托的應用啟動的時候綁定服務,這樣可以在不使用的時候避免浪費系統資源。

  源碼下載

總結

  值得注意的是,Android下的Service組件也是運行在主線程中的,所以一些Android4.0+無法在主線程上進行的操作,在Service中也必須另外開啟線程來完成,如訪問網絡,還可以使用繼承Service的子類IntentService來實現,這個內容會在之后的博客中介紹。Android系統本身還提供了大量的Service組件,開發人員可以通過這些系統Service來操作Android系統本身,這不屬於本篇博客的范疇,以后再慢慢詳解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM