[coolxing按: 轉載請注明作者和出處, 如有謬誤, 歡迎在評論中指正.]
Service是android中的服務元件, 經常用來執行一些運作在背景的耗時操作. 使用一個Service需要繼承Service類, 并根據需要重寫生命周期方法. Service的生命周期如下:

|-- public abstract IBinder onBind (Intent intent): 該方法是一個抽象方法, 是以Service子類必須實作這個方法. 它傳回一個IBinder對象, 應用程式可以通過這個對象與Service元件通信(關于這一點, 其後會有詳細的講解), 以bindService()方式啟動Service時回調該方法.
|-- public void onCreate (): 當Service第一次被建立後回調的方法.
|-- public void onDestroy (): Service關閉之前回調的方法.
|-- public int onStartCommand (Intent intent, int flags, int startId): 以startService(Intent intent)的方式啟動Service時, 系統都會回調該方法.
|-- public boolean onUnbind (Intent intent): 當所有綁定該Service的元件都斷開連接配接時回調該方法.
從圖中可以看出, Service可以有兩種啟動方式:
1. 以startService(Intent intent)的方式啟動. 此時啟動的Service與調用者之間沒有關聯, 即使調用者已經退出, Service仍然可以繼續運作, 而且調用者和Service之間無法進行資料交換和通信. 如果需要停止Service的運作, 隻能調用Context類的stopService(intent)方法, 或者由Service本身調用其stopSelf()等方法.
2. 以bindService(Intent service, ServiceConnection conn, int flags)的方式啟動.
此時調用者與Service綁定在一起, 如果調用者退出, 則Service也随之退出, 而且調用者和Service之間可以進行資料交換或通信.
根據調用者和Service是否在一個應用程式内, 可以将調用者和Service之間的通信分為程序内通信和程序間通信.
a. 程序内通信. bindService(Intent service, ServiceConnection conn, int flags)方法的第二個參數為ServiceConnection對象, 最後一個參數通常可以是Service.BIND_AUTO_CREATE. ServiceConnection是一個接口, 該接口包含2個方法:
|-- onServiceConnected(ComponentName name, IBinder service): 該方法在調用者和Service成功綁定之後由系統回調.
方法中的第一個參數ComponentName是所綁定的Service的元件名稱, 而IBinder對象就是Service中onBinder()方法的傳回值. 要實作調用者和Service之間的通信, 隻需要調用IBinder對象中定義的方法即可.
|-- onServiceDisconnected(ComponentName name): 該方法在調用者解除和Service的綁定之後由系統回調.
以下是利用Service實作程序内通信的一個例子.
首先自定義Service類:
public class MyService extends Service {
public class MyBinder extends Binder {
/**
* 擷取Service的運作時間
* @return
*/
public long getServiceRunTime() {
return System.currentTimeMillis() - startTime;
}
}
private long startTime;
/**
* MyBinder是Binder的子類, 而Binder實作了IBinder接口.
*/
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
@Override
public void onCreate() {
super.onCreate();
startTime = System.currentTimeMillis();
}
}
然後在activity中綁定上述的Service:
public class MainActivity extends Activity {
private MyBinder binder = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 建立一個指向MyService的intent
Intent intent = new Intent("cn.xing.action.my_service");
this.bindService(intent, new MyServiceConnection(),
Service.BIND_AUTO_CREATE);
Button button = (Button) this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (binder != null) {
Toast.makeText(getApplicationContext(), "MyService已經運作了" + binder.getServiceRunTime()
+ "毫秒", Toast.LENGTH_LONG).show();
}
}
});
}
/**
* 實作ServiceConnection接口
*
* @author xing
*
*/
private final class MyServiceConnection implements ServiceConnection {
/**
* 和MyService綁定時系統回調這個方法
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// MyService中的onBinder()方法的傳回值實際上是一個MyBinder對象, 是以可以使用強制轉換.
binder = (MyBinder) service;
}
/**
* 解除和MyService的綁定時系統回調這個方法
*/
@Override
public void onServiceDisconnected(ComponentName name) {
// 解除和MyService的綁定後, 将binder設定為null.
binder = null;
}
}
}
b.程序間通信. 調用者和Service如果不在一個程序内, 就需要使用android中的遠端Service調用機制.
android使用AIDL定義程序間的通信接口. AIDL的文法與java接口類似, 需要注意以下幾點:
1. AIDL檔案必須以.aidl作為字尾名.
2. AIDL接口中用到的資料類型, 除了基本類型, String, List, Map, CharSequence之外, 其他類型都需要導包, 即使兩種在同一個包内. List和Map中的元素類型必須是AIDL支援的類型.
3. 接口名需要和檔案名相同.
4. 方法的參數或傳回值是自定義類型時, 該自定義的類型必須實作了Parcelable接口.
5. 所有非java基本類型參數都需要加上in, out, inout标記, 以表明參數是輸入參數, 輸出參數, 還是輸入輸出參數.
6. 接口和方法前不能使用通路修飾符和static, final等修飾.
下面通過一個例子來示範android遠端Service調用機制的各個步驟:
1. 建立remoteService項目.
2. 在cn.xing.remoteservice包下定義aidl檔案--IRemoteService.aidl:
package cn.xing.remoteservice;
interface IRemoteService{
int getServiceRunTime();
}
Eclipse的ADT插件會在gen目錄的cn.xing.remoteservice包下自動根據aidl檔案生成IRemoteService接口.
接口中有一個static的抽象内部類Stub, Stub類繼承了Binder類并實作了IRemoteService接口. Stub類有如下的靜态方法:
public static cn.xing.remoteservice.IRemoteService asInterface(android.os.IBinder obj)
該方法接受一個IBinder對象, 傳回值是IRemoteService的instance, 通過這個instance我們就可以調用IRemoteService中定義的方法了.
3. 在remoteService項目的cn.xing.remoteservice包下建立遠端服務類RemoteService:
public class RemoteService extends Service {
private long startTime;
/**
* IRemoteService.Stub類實作了IBinder和IRemoteService接口
* 是以Stub的子類對象可以作為onBinder()方法的傳回值.
* @author xing
*
*/
public class MyBinder extends IRemoteService.Stub {
@Override
public long getServiceRunTime() throws RemoteException {
return System.currentTimeMillis() - startTime;
}
};
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
@Override
public void onCreate() {
super.onCreate();
startTime = System.currentTimeMillis();
}
}
4. 建立一個新的android項目invokeRemoteService, 并複制remoteService項目中的aidl檔案(連同包結構一起複制)到invokeRemoteService項目中.
5. 在invokeRemoteService項目中綁定遠端服務, 并調用遠端方法.
public class MainActivity extends Activity {
private IRemoteService iRemoteService = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 建立一個指向RemoteService的intent
Intent intent = new Intent("cn.xing.action.remote_service");
this.bindService(intent, new MyServiceConnection(),
Service.BIND_AUTO_CREATE);
Button button = (Button) this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (iRemoteService != null) {
try {
Toast.makeText(getApplicationContext(), "MyService已經運作了" + iRemoteService.getServiceRunTime()
+ "毫秒", Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
/**
* 實作ServiceConnection接口
*
* @author xing
*
*/
private final class MyServiceConnection implements ServiceConnection {
/**
* 和RemoteService綁定時系統回調這個方法
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 此處不能使用強制轉換, 應該調用Stub類的靜态方法獲得IRemoteService接口的執行個體對象
iRemoteService = IRemoteService.Stub.asInterface(service);
}
/**
* 解除和RemoteService的綁定時系統回調這個方法
*/
@Override
public void onServiceDisconnected(ComponentName name) {
// 解除和RemoteService的綁定後, 将iRemoteService設定為null.
iRemoteService = null;
}
}
}
附件中包含程序内通信和程序間通信例子的源碼, 需要的朋友可自行下載下傳檢視.