一、Service簡介
Android中服務是運行在後臺的東西,級別與activity差不多。既然說service是運行在後臺的服務,那麼它就是不可見的,沒有界面的東西。你可以啓動一個服務Service來播放音樂,或者記錄你地理信息位置的改變,或者啓動一個服務來運行並一直監聽某種動作。Service和其他組件一樣,都是運行在主線程中,因此不能用它來做耗時的請求或者動作。你可以在服務中開一個線程,在線程中做耗時動作。
二、本地服務和遠程服務
Android服務分爲本地服務(Local Service)和遠程服務(Remote Service),這兩類服務的區別在於創建服務的客戶端(通常爲Activity)與所創建的服務是否運行在同一進程中,如下圖所示。
圖1 圖2
如圖1,一個Activity調用startService或bindService,創建出服務,若該服務與創建服務的Activity運行在同一進程中,則把這種服務成爲本地服務。並且本地服務只能在創建該服務的應用程序內部使用,當應用程序終止時,本地服務也一同終止。
與本地服務不同,遠程服務運行在單獨的進程中,所以當主應用程序終止時,遠程服務仍然會繼續運行。若要求在應用程序的主要部分(如Activity)終止時,服務仍要繼續運行,執行某些特定任務,此時就該考慮使用遠程服務。由於遠程服務在程序退出時仍在運行,會繼續消耗系統資源,所以在設計遠程服務時一定要認真考慮、慎重處理。
三、生命週期
在Android中,服務是應用程序組件,具有一定的生命週期,如下圖所示。
圖3
從圖3我們可以看到,startService()和bindService()兩種啓動方式的生命週期有所區別,主要有以下幾點不同:
1、目的不同,由startService()啓動的服務目的只是服務的啓動與終止,而bindService()啓動的服務是爲了服務的遠程控制。
2、使用場合不同,使用startService()方法啓用服務,調用者與服務之間沒有關聯,即使調用者退出了,服務仍然運行。使用bindService()方法啓用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止。
3、啓動流程不同,如果採用startService()方法啓動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接着調用onStartCommand ()方法,如果調用startService()方法前服務已經被創建,多次調用startService()方法並不會導致多次創建服務,但會導致多次調用onStartCommand ()方法,採用startService()方法啓動的服務,只能調用stopService()方法結束服務,服務結束時會調用onDestroy()方法;
如果採用bindService()方法啓動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接着調用onBind()方法,這個時候調用者和服務綁定在一起,調用者退出了,系統就會先調用服務的onUnbind()方法,接着調用onDestroy()方法。如果調用bindService()方法前服務已經被綁定,多次調用bindService()方法並不會導致多次創建服務及綁定(也就是說onCreate()和onBind()方法並不會被多次調用)。如果調用者希望與正在綁定的服務解除綁定,可以調用unbindService()方法,調用該方法也會導致系統調用服務的onUnbind()-->onDestroy()方法。
基於這兩種啓動服務方式的特點,我們通常不會單獨使用某一種啓動方式,經常是將兩者結合起來使用。startService服務啓動方式比較容易理解,在此不再贅述,下面着重介紹服務的綁定。
四、服務的綁定
本地服務和遠程服務的綁定是不一樣的,我們分別進行介紹。
(1)本地服務的綁定
在本地服務中,服務與使用服務的客戶端程序運行在同一進程中,本地服務的綁定實際上是指客戶端程序獲取了待綁定服務的一個引用。當綁定完成後,客戶端即獲取服務引用,通過該引用,客戶端即可調用該服務提供的成員變量和各種方法。
我們通過一個demo來了解本地服務如何工作的。程序中主要有兩個類,LocalService.java是本地服務類,MainActivity.java是使用服務的Activity,在Manifest文件中註冊service
<serviceandroid:name="com.example.localservicedemo.LocalService" ></service>
創建服務類LocalService.java
package com.example.localservicedemo;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class LocalService extends Service {
privatestatic final String TAG = "kingfong";
@Override
publicvoid onCreate() {
//TODO Auto-generated method stub
super.onCreate();
Log.d(TAG,"LocalService-----------onCreate()");
}
@Override
publicint onStartCommand(Intent intent, int flags, int startId) {
//TODO Auto-generated method stub
Log.d(TAG,"LocalService-----------onStartCommand()");
returnsuper.onStartCommand(intent, flags, startId);
}
@Override
publicvoid onDestroy() {
//TODO Auto-generated method stub
super.onDestroy();
Log.d(TAG,"LocalService-----------onDestroy()");
}
publicvoid printLog(){
Log.d(TAG,"LocalService-----------printLog()");
}
@Override
publicIBinder onBind(Intent arg0) {
//TODO Auto-generated method stub
returnmyBinder;
}
publicclass LocalBinder extends Binder{
publicLocalService getService(){
returnLocalService.this;
}
}
privateLocalBinder myBinder = new LocalBinder();
}
1、在MainActivity中設置兩個按鈕事件,bind和ubind,點擊bind調用bindService(),嘗試綁定LocalService。
@Override
public void onClick(View v){
switch(v.getId()){
case R.id.bind_service:
bindService();
break;
case R.id.ubind_service:
ubindService();
break;
default:
break;
}
}
public void bindService(){
bindService(newIntent(MainActivity.this,LocalService.class),conn,Context.BIND_AUTO_CREATE);
mIsbind = true;
}
public void ubindService(){
if(mIsbind){
unbindService(conn);
mIsbind = false;
}
}
bindService(Intent service, ServiceConnectionconn,int flag)API的第一個參數是服務的Intent,第二個參數是服務客戶端用於處理綁定連接的對象,第三個參數Context.BIND_AUTO_CREATE是一個自動生成本地服務的標記,當帶綁定的服務不存在時使用。若LocalService尚未運行,在實施綁定之前先生成LocalService。
2、在待綁定的服務生成之後,調用服務的onBind()回調方法,返回LocalBinder對象,Activity使用該對象與LocalService連接。
@Override
publicIBinder onBind(Intent arg0) {
//TODO Auto-generated method stub
returnmyBinder;
}
publicclass LocalBinder extends Binder{
publicLocalService getService(){
returnLocalService.this;
}
}
privateLocalBinder myBinder = new LocalBinder();
3、如果處理服務綁定的對象(此處指LocalBinder對象)創建成功,就會調用onServiceConnected(ComponentName cname, IBinderservice)方法,第二個參數保存着onBind()生成的LocalBinder對象的引用。
private ServiceConnection conn =new ServiceConnection(){
@Override
publicvoid onServiceConnected(ComponentName cname, IBinder service) {
//TODO Auto-generated method stub
LocalBinderbinder = (LocalBinder)service;
mLocalService= binder.getService();
}
@Override
publicvoid onServiceDisconnected(ComponentName cname) {
//TODO Auto-generated method stub
mLocalService= null;
Log.d(TAG,"Service-------Disconnected");
}
};
4、保存LocalService對象的引用到Activity的mLocalService成員變量中,本地服務綁定完成。
在服務綁定之後,Activity即可通過保存在mLocalService成員變量中的LocalService對象的引用,訪問服務的成員變量和相關方法。如調用LocalService中的printLog()方法。
mLocalService.printLog();
根據以上詳細分析可總結爲如下過程:
bindService()-------->onBind(),傳遞Binder對象--------> onServiceConnected()-------->通過傳遞的Binder對象獲取服務對象-------->調用service中定義的成員變量和方法
(2)遠程服務的綁定
對遠程服務而言,其在單獨的進程中,Activity若想控制服務,必須使用IPC機制(後續介紹)。遠程服務綁定是指設置相關的連接設置,以便運行Binder IPC。
在Binder IPC通信中,服務於activity交換數據時,需要經歷Marshlling/Unmarshalling這一過程,爲此,就要使用AIDL(Android Interface Definition Language,android接口定義語言),用於約束兩個進程間的通信規則,共編譯器生成代碼,用來實現android設備上兩個進程間的通信(Interprocess Communication, IPC)。
下面我們仍然通過一個demo來分析遠程服務的綁定流程,遠程服務的代碼構成與本地服務是有所區別的,除了RemoteService.java和MainActivity.java之外,還有IRemoteService.aidl以及由該文件自動生成的IRemoteService.java文件。
在IRemoteService.aidl中定義了一個IRemoteService接口和count()方法
package com.example.remoteservicedemo;
interface IRemoteService
{
void count();
}
AIDL文件創建好後,刷新下工程,在gen目錄下可以看到和AIDL名一樣的java文件,具體內容不詳細介紹。
另外,在manifest文件中也有所區別,如下所示
<serviceandroid:name="com.example.remoteservicedemo.RemoteService"android:process=":remote" android:exported="false">
<intent-filter>
<actionandroid:name="com.example.remoteservicedemo.IRemoteService"/>
</intent-filter>
</service>
android:process=":remote",代表在應用程序裏,當需要該service時,會自動創建新的進程。而如果是android:process="remote",沒有“:”分號的,則創建全局進程,不同的應用程序共享該進程。
1、 MainActivity:請求RemoteService連接
在MainActivity同樣設置兩個按鈕事件,點擊bind,執行bindService()API
publicvoid bindService(){
bindService(newIntent(IRemoteService.class.getName()),conn,Context.BIND_AUTO_CREATE);
mIsbound= true;
}
publicvoid unbindService(){
if(mIsbound){
unbindService(conn);
mIsbound= false;
Log.d(TAG,"unbindService()");
}
}
2、RemoteService:具體實現服務方法,並提供用於通信的Binder對象
服務啓動後,會根據服務的生命週期,依次調用onCreate、onBind方法。onBind方法的主要作用是生成用於處理Binder IPC的Binder對象,並將其返回給系統。
服務的綁定對象是由IRemoteService.java的IRemoteService.Stub類生成的,實現定義在IRemoteService接口中的count()方法。
@Override
publicIBinder onBind(Intent arg0) {
//TODO Auto-generated method stub
returnmBinder;
}
privatefinal IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public void count() throws RemoteException {
//TODO Auto-generated method stub
mHandler.sendMessage(mHandler.obtainMessage(PRINT_LOG));
}
};
3、MainActivity:生成執行服務於Binder IPC的代理對象
與本地服務類似,不同的是將Binder對象傳遞給IRemoteService.Stub.asInterface函數,並使用它生成與RemoteService服務綁定在一起的服務代理對象IRemoteService.Stub.Proxy,最後將其保存到mService成員變量中
private ServiceConnection conn =new ServiceConnection(){
@Override
publicvoid onServiceConnected(ComponentName cname, IBinder service) {
//TODO Auto-generated method stub
mService= IRemoteService.Stub.asInterface(service);
}
至此,RemoteService的IRemoteService接口的綁定就完成了,activity可以通過保存在mService中的服務代理對象,調用服務中的方法。
4、MainActivity:使用服務代理對象,調用服務中的count()方法
if(mService!=null){
try{
mService.count();
}catch(RemoteExceptione){}
}
5、Binder IPC:服務代理對象(IRemoteService.Stub.Proxy)向服務Binder對象(IRemoteService.Stub)傳遞Binder IPC數據
IRemoteService.Stub.Proxy遠程代理對象通過Binder IPC向IRemoteService.Stub服務Binder對象傳遞數據,以處理4中對count()代理方法的調用。
6、RemoteService:調用RemoteService的count() stub方法
IRemoteService.Stub服務Binder對象獲取Binder IPC數據後,會調用2中實現的count() stub方法,將服務進程的ID返回給activity。
至此,我們對遠程服務的綁定及綁定後調用相關接口特定方法做了簡單介紹。
參考文獻:
【1】金泰延、宋亨周、樸知勳、李白、林起永著 Android框架揭祕
【2】http://www.cnblogs.com/zhangdongzi/archive/2012/01/08/2316711.html
【3】http://www.cnblogs.com/onlylittlegod/archive/2011/05/15/2046652.html