本文主要介紹Service的概念及作用、使用(包括長時間運行的服務、應用內交互的服務、應用間交互的服務的分別舉例、生命周期、使用場景)、特殊的Service。
示例代碼見ServiceDemo,示例APK見:TrineaAndroidDemo.apk。
由於ANR對Activity和BroadcastReceiver響應時間的限制(Activity對事件響應不超過5秒,BroadcastReceiver執行不超過10秒),使得在其中都不適合執行較耗時操作,這樣像網絡、數據庫、復雜計算這類耗時操作的執行就需要一個組件來承擔。Service作為Android四大組件之一,其功能之一就是耗時操作的執行,主要功能如下:
a. 執行需要長時間運行的操作,這個操作不與用戶進行交互,如網絡下載、大文件I/O、復雜計算。
b. 應用內或應用間數據通信,Android每個應用程序都在自己的dalvik虛擬機中運行,一個應用是不允許訪問其他應用的內存信息的,為此Android引入了Content Provider在不同應用間共享數據,BroadcastReceiver廣播信息給不同應用程序,但Content Provider更多用於數據的共享,BroadcastReceiver廣播的信息會被所有應用接收較耗費系統資源,對於兩個應用間動態的進行交互還需要通過Service來完成。
2、使用
(1) startService啟動不可進行交互的Service
a. 示例代碼及介紹

public class MyService extends Service { @Override public void onCreate() { super.onCreate(); Toast.makeText(this, "Service Create", Toast.LENGTH_SHORT).show(); } @Override public void onDestroy() { Toast.makeText(this, "Service Destroty", Toast.LENGTH_SHORT).show(); super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "Service Start", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { return null; } }
服務都必須在AndroidManifest.xml文件中注冊<service android:name=".MyService"/>,在Activity中定義對象private Intent myServiceIntent = new Intent(this, MyService.class);在onCreate函數中startService(myServiceIntent)啟動服務; onDestroy函數中stopService(myServiceIntent)關閉服務;
b. 生命周期
通過startService啟動服務,若服務未啟動,會先執行onCreate函數(若服務已啟動則不執行此函數),再執行onStartCommand函數。由此可知多次調用startService傳入相同參數不會啟動多個服務(onStartCommand函數會執行多次),所以最終只需要調用一次stopService或stopSelf函數停止服務;我們可以將service的處理邏輯放入onStartCommand函數中。服務一直運行,在程序退出后服務也不會停止,直到stopService或stopSelf函數被調用,當然可能被系統回收。
對於onStartCommand的返回值,若返回START_STICKY表示服務通過顯式調用啟動或停止,若返回START_NOT_STICKY
orSTART_REDELIVER_INTENT表示服務僅在有請求發送過來處理時才處於運行狀態。
c. 使用場景
因為這種方式Service無法與外部進行方便的動態交互,所以適合做后台服務,如網絡下載(用戶通過Intent傳入Url到Service,推薦使用IntentService).
(2) bindService啟動的Service應用內交互
a. 示例代碼及介紹
在上面的方式中Context可以通過Intent向Service傳入簡單的信息,但是如果希望調用Service的接口進行操作或是獲取Service的屬性則無法實現,這里我們可以通過bindService實現,如下先定義自己的服務

public class MyService extends Service { private int count; private MyBinder myBinder = new MyBinder(); @Override public void onCreate() { Toast.makeText(this, "Service onCreate", Toast.LENGTH_SHORT).show(); count = 0; super.onCreate(); } @Override public void onDestroy() { Toast.makeText(this, "Service onDestroy", Toast.LENGTH_SHORT).show(); super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } /** * 服務被綁定時調用 * 返回值用於讓調用者和服務通信,傳入ServiceConnection的public void onServiceConnected(ComponentName name, IBinder service)函數第二個參數 */ @Override public IBinder onBind(Intent intent) { return myBinder; } public int getCount() { return count; } public int increaseCount() { return ++count; } public int decreaseCount() { return --count; } public class MyBinder extends Binder { MyService getService() { return MyService.this; } } }
從上可以看出我們重寫onBind函數並返回自己的Binder用於調用者和服務之間通信。下面我們在調用者中進行調用

private MyService myService; private Intent myServiceIntent; private ServiceConnection con = new ServiceConnection() { /** * 服務所在進程被kill或是crash時系統調用,而不是unbindService時調用 */ @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(getApplicationContext(), "Service disconnect", Toast.LENGTH_SHORT).show(); } /** * 服務連接時調用,若已經連接不進行調用 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { myService = ((MyBinder)service).getService(); Toast.makeText(getApplicationContext(), "Service Connect", Toast.LENGTH_SHORT).show(); } };
接着調用bindService(myServiceIntent, con, Context.BIND_AUTO_CREATE);綁定服務,綁定成功返回true。這時會執行ServiceConnection對象的onServiceConnected函數為myService變量賦值。接着我們就可以通過myService.increaseCount();操作Service的屬性,myService.getCount()獲得Service的屬性,這樣我們就成功和Service進行了通信。在不需要通信時通過unbindService(con);解除服務綁定。
b. 生命周期
通過bindService綁定服務,若服務未啟動,會先執行Service的onCreate函數,再執行onBind函數,最后執行ServiceConnection對象的onServiceConnected函數。若服務已啟動但尚未綁定,先執行onBind函數,再執行ServiceConnection對象的onServiceConnected函數。若服務已綁定成功,則直接返回。這里不會自動調用onStartCommand函數。
通過unbindService解除綁定服務,若已綁定成功,會先執行Service的onUnbind函數,再執行onDestroy函數,注意這里不會執行ServiceConnection對象的onServiceDisconnected函數,因為該函數是在服務所在進程被kill或是crash時被調用。若服務尚未綁定系統會報服務尚未注冊異常,我們可以通過如下代碼解決
if (myService != null) { unbindService(con); myService = null; }
與startService啟動的Service不同,若服務通過bindService啟動並且沒有通過startService啟動過后,則在連接斷開時服務就會自動解綁(onUnbind)並終止(onDestroy),而在調用者(Activity)退出后會自動斷開連接,所以這時服務會自己解綁並終止。若存在某個組件綁定了該服務,則調用該服務的stopService不會停止服務。
c. 使用場景
應用內通信,如音樂播放器,在服務中控制播放器的播放、暫停、停止,在Activity中通過對服務操作控制播放器。
(3) bindService啟動的Service應用間交互——AIDL
在Android AIDL應用間交互中 詳細介紹見Android AIDL交互。
通過上面的三個例子我們可以看出Context.startService()
和Context.bindService()
的區別
a. bindService啟動的Service可以和Context進行交互,而startService啟動的Service不可以交互,因而使用場景也不同
b. 執行的生命周期不同
3、特殊的Service
(1). 異步服務IntentService
默認Service是運行在主線程內的 ,如果在Service內運行一個耗時操作就會阻塞主線程,可能導致ANR,為此我們可以在Service中自己新建線程去執行耗時操作,不過Android系統引入了IntentService方便的解決了這個問題, IntentService會啟動一個工作線程去完成用戶onHandleIntent中定義的操作,需要注意的是對於同一個IntentService的多次請求(startService調用),在同一個線程中處理,一次只會執行一個請求的onHandleIntent函數。對於不同IntentService的同時請求,在不同的線程中處理,所以每個請求的onHandleIntent函數可以並發執行。示例代碼如下:

public class MyIntentService extends IntentService { public MyIntentService(){ super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { try { System.out.println("IntentService1 Begin Sleep. " + "Thread name: " + Thread.currentThread().getName() + ", Thread Id: " + Thread.currentThread().getId()); Thread.sleep(3000); System.out.println("IntentService1 End. "); } catch (InterruptedException e) { e.printStackTrace(); } } }
在AndroidManifest.xml文件中注冊服務<service android:name=".MyIntentService"/>,在Activity中定義service對象private Intent myIntentServiceIntent = new Intent(ServiceDemo.this, MyIntentService.class);在onCreate函數中startService(myIntentServiceIntent)啟動服務;
從上面我們可以看出
a. IntentService只需要重定義onHandleIntent函數並定義一個無參構造函數(xml中服務注冊初始化時使用)即可。
b. IntentService服務在onHandleIntent執行結束后會自動關閉。
IntentService和普通Service的區別如下:
a. 普通Service運行在主線程中,IntentService運行在一個工作線程中不會阻塞主線程。
b. 普通Service需要手動調用停止接口,IntentService自動停止。
c. IntentService的onStartCommand函數根據mRedelivery屬性值返回START_REDELIVER_INTENT或START_NOT_STICKY,而普通Service自定義返回。
參考:
http://developer.android.com/reference/android/app/Service.html
http://developer.android.com/guide/components/services.html
http://developer.android.com/reference/android/app/IntentService.html