Table of contents


Android Service可以視為Windows的系統服務或Linux的Daemons,可以長時間在存在系統中(不一定要直在活動狀態),而且,他有自已的生命周期,不會隨著Activity的消滅而消滅。

Android有兩種類型的services

  1. Local Service
  2. 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()的傳回值來改變,有效的傳回值有:

  1. START_STICKY : 預設的行為,service被系統kill掉後或做完工作後,系統會restart service,一般要停止service話,是透過stopService(),或stopSelf()
  2. START_NOT_STICKY : 如果service做完工作後,系統會stop service,而不是restart service,這個很適用來做周期的工作(比如說每15分鐘檢查一次mail box),而不是一直不斷執行的任務,下次收到intent
  3. 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,通常是在

  1. 希望service的生命周期activity跟一致(一般的service啟動後,就會一直執在背景執行,直到收到stop或被系統kill)
  2. 不希望service在沒預期的情況下被系統kill(系統會在資料不足情況下殺掉process,但可以透過startForeground()降低被殺掉的機率)
  3. 希望在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