Android Service 101
Table of contents
Android Service可以視為Windows的系統服務或Linux的Daemons,可以長時間在存在系統中(不一定要直在活動狀態),而且,他有自已的生命周期,不會隨著Activity的消滅而消滅。
Android有兩種類型的services
- Local Service
- Remote Service (透過AIDL定義)
Local Service是應用程式自已內部要使用的service,而Remote Service則可提供給其他應用程式使用。Service執行時,一樣是執行在Main Thread(UI Thread),所以,service會影響到UI的操作效能,如果service執行的時間很短,那問題不大,一但service要執行很久的時間,那應該要採用背景服務的方式來處理。
如果想讓serivce裡的worker thread只有一個instance,可以在oncreate建立,這樣就能讓thread instance(或者是ExecutorService)只有一份
Android有提供另一個跟Service一樣,適合在背景作業的機制叫Broadcast Receiver,至於何時該用Servce,何時該用Broadcast Receiver可以參考這篇文章
Service
下例是一個簡單的service,onBind()
如果不是要做Bound
Service的話,就直接return null即可,而onStartCommand()
則是service主要要實作的部份
,依收到的intent去決定要實作那些功能,如果比較耗時的功能,記得用背景作業處理處理,因為service並不是執行在另一個thread。
如果想讓Service執行成另一個獨立的process,可以透過設定service的xml中的process屬性,像這樣
<service
android:name="WordService"
android:process=":my_process"
android:icon="@drawable/icon"
android:label="@string/service_name"
>
</service>
Service的啟動與停止是透過Context中的startService(Intent)
及stopService(Intent service)
,Activity,Service跟Application都是繼承自Context,所以可以直接呼叫。
比較值得注意的地方為onStartCommand()
的傳回值部份,這個跟Service Life
cycle有關。在系統需要資源時,系統可能會把service給kill掉,而之後等系統比較有空時,
會再自動把service給叫回來,但這個行為,可以透過指定onStartCommand()
的傳回值來改變,有效的傳回值有:
- START_STICKY : 預設的行為,service被系統kill掉後或做完工作後,系統會restart service,一般要停止service話,是透過stopService(),或stopSelf()
- START_NOT_STICKY : 如果service做完工作後,系統會stop service,而不是restart service,這個很適用來做周期的工作(比如說每15分鐘檢查一次mail box),而不是一直不斷執行的任務,下次收到intent
- START_REDELIVER_INTENT : 跟START_NOT_STICKY很像,這個適合用來做失敗時會再重做n次的工作,它會在工作沒能成功做完時再次呼叫自己
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
Intent Service
IntentService會產生一個額外的worker thread所以不需要在IntentService裡另外使用Thread或AsncTask,但也因為這樣,在IntentService裡做UI動作時,要確定是發生在MainThread
如果要在Intent Service裡對UI進行操作,可以透過Handler的機制
public class MyService extends IntentService {
public MyService() {
super("MyIntentService"); // constructor一定要有一個default的(無參數的)
}
private Handler handler = new Handler(); // 在這邊宣告的Handler,是attach在Main Thread上面的
@Override
protected void onHandleIntent(Intent intent) {
// 透過attach在Main Thread上面的的handler post的method內的動作,可以對UI進行操作
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MyService.this, "Some Message" , Toast.LENGTH_SHORT).show();
}
});
}
}
Intent Serivce的onStartCommand()
實作如下,由實作可以知道Intent
Service是用來處理一次性的服務,發一次intent,就把該intent要做的事做完後就停止service,而不是讓service一直在執行
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
Remote Service
Remote Service跟寫一般RMI(RPC)的程式流程上差不多,定義interface(AIDL),產生stub code,實作stub code,實作完是直接使用service,而不是透過intent呼叫。也跟一般的RMI一樣,你必須認真面對複雜型別的Serialization問題。
Bound Service
Bound Service中的Bound的意思是要跟某個Activity”綁定”,也就是說,這個service的生命周期會受到Activity所控制。
- binding是非同步的,也就是,service ready時,才會call back ServiceConnection.onServiceConnected() method;
- Binder不要宣告成非static的inner class,不然會發生service leaking的問題
通用型的Local Service Binder,可適用於大部份的情況
public class LocalBinder<S> extends Binder {
private WeakReference<S> service;
public LocalBinder(S service){
this.service = new WeakReference<S>(service);
}
public S getService() {
return service.get();
}
}
那何時需要用到Bound Service,通常是在
- 希望service的生命周期activity跟一致(一般的service啟動後,就會一直執在背景執行,直到收到stop或被系統kill)
- 不希望service在沒預期的情況下被系統kill(系統會在資料不足情況下殺掉process,但可以透過startForeground()來降低被殺掉的機率)
- 希望在activity內可以直接對service進行更多操作 (local service要透過intent啟動,停止或進行其他command)
開機後自動啟動的Service
開機後自動啟動的Service算是很常見的應用,很多的app都有這樣的需求,要達到這樣的功能,可以透過BroadcastReceiver去接收android.intent.action.BOOT_COMPLETED
的系統事件,這個事件會在開機完成後被廣播一次。
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, MyService.class);
context.startService(service);
}
}
<receiver android:name="MyReciever" >
<intent filter="" >
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent>
</receiver>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
如果程式放在SD Card上,recive的xml設定檔還要去註冊android.intent.action.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
,另外,在Android 3.0之後,程式至少要被執行過一次才會接到這個廣播事件。
Resources
- http://www.ozdroid.com/#!BLOG/2010/12/19/How_to_make_a_local_Service_and_bind_to_it_in_Android - service binding的介紹
- http://developer.android.com/resources/articles/multitasking-android-way.html - 關於android multitasking的介紹,可以更瞭解service的設計與使用的時機
- http://www.vogella.de/articles/AndroidServices/article.html