前言
本篇博客聊一下Android下的Service組件,對於Service組件,有點類似於Windows下的服務。Service是Android四大組件中與Activity最相似的組件,它們的區別在於:Service一直在后台運行,它沒有用戶界面。一旦Service被啟動起來之后,它就與Activity一樣,也具有自己的生命周期。
在開發過程中,對於Activity與Service的選擇標准是:如果某個程序組件需要在運行時向用戶呈現某種界面,或者該程序需要與用戶交互,就需要使用Activity,否則就應該考慮使用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是一個與Activity類似的Android組件,所以它的開發步驟大致也為一下幾步:
- 開發一個服務類,需要繼承Service或者IntentService。
- 在AndroidManifest清單文件中注冊Service組件。
- 在一個Android組件中啟動這個開發的Service組件。
- 服務使用完成之后,需要停止這個服務。
啟動服務必須實現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中。
相信看完以上的內容,對Android下Service組件有了一定的了解,但是對於選用startService()還是使用bindService(),有一些原則需要講明。如果僅僅是想要啟動一個后台服務長期駐留在內存中執行某項任務,那么僅使用startService()啟動一個服務即可。但是如果想要與正在運行的服務進行交互,一種方式是使用broadcast,這個以后再介紹,另外一種方式就是使用bindService()綁定一個服務到組件上,使用broadcast的缺點是如果數據交互頻繁,容易造成性能上的問題,並且BroadcastReceiver本身執行代碼的時間不確定,也許代碼執行到一半,后面的代碼將不被執行,但是使用bindService()綁定服務即沒有這些問題。另外,如果一個服務是依托於某項應用的,那么也最好使用綁定服務,在依托的應用啟動的時候綁定服務,這樣可以在不使用的時候避免浪費系統資源。
總結
值得注意的是,Android下的Service組件也是運行在主線程中的,所以一些Android4.0+無法在主線程上進行的操作,在Service中也必須另外開啟線程來完成,如訪問網絡,還可以使用繼承Service的子類IntentService來實現,這個內容會在之后的博客中介紹。Android系統本身還提供了大量的Service組件,開發人員可以通過這些系統Service來操作Android系統本身,這不屬於本篇博客的范疇,以后再慢慢詳解。