啟動型Service

Service 本身就是處於背景中執行的 Context, 與在前景執行的 Context 不一樣. 前景是負責處理 UI/UX, 而背景中執行的任務, 通常是與 UI/UX 無關的項目, 這樣一來, 就不會影響到使用者的使用體驗, 也能同步在運作在背景中.
啟動型的 Service, 就是可以被 Context 物件實體, 以 startService() 的方式啟動 Service 物件的生命週期. 換言之, 可以由前景的 Activity, 或是另一個已經在背景執行的 Service 物件實體來啟動的 Service.
一但開始執行的 Service 物件實體及其生命週期的表現, 並不會因為啟動的 Activity 的生命週期改變而有所影響. 例如: 當啟動 Service 的 Activity 生命週期結束, 並不會導致 Service 也結束生命週期.

啟動Service


啟動機制模式的Service, 是藉由Context.startService()所啟動起來的Service, 啟動之後的生命週期進入到初始化處理的onCreate()及onStartCommand(). 之後的模式都是透過Context.startService()來觸發onStartCommand()來進行互動機制. 即使Context已經結束生命週期, Service仍然會繼續運作, 除非Context.stopService()被呼叫, 才會觸發onDestroy()來結束Service.

建立 Service

建議利用 File → New → Service → Service 方式建立一個Service, 將會自動在AndroidManifest.xml中建立該Service的元件設定. 如果是以自訂Java Class的方式的話, 則必須手動方式建立.
MyService2.java

public class MyService2 extends Service {
    public MyService2() {
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}
								
開始要處理一下這段程式碼, 因為其預設的模式是Bound Service.
1. 將onBind()方法內容修改, throw的拋出例外敘述句刪掉, 加上return null; 即可
2. 加上Override其onCreate(), onStartCommand()及onDestroy()方法.

public class MyService2 extends Service {
    public MyService2() {
        Log.i("brad", "MyService2()");
    }
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("brad", "MyService2:onBind()");
        return null;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("brad", "MyService2:onCreate()");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("brad", "MyService2:onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("brad", "MyService2:onDestroy()");
    }
}							
								

此時搭配的MainActivity加上兩個Button來觸發方法:
1. test2(): 呼叫startService()
2. test3(): 呼叫stopService()
MainActivity.java 中

public void test2(View view){
    Intent intent = new Intent(this, MyService2.class);
    startService(intent);
}
public void test3(View view){
    Intent intent = new Intent(this, MyService2.class);
    stopService(intent);
}								
透過LogCat可以清楚的觀察到:
1. 首次觸發startService(), 會建立MyService2物件實體, 直接依序執行onCreate()及onStartCommand()
2. 之後再觸發startService(), 則只會觸發startCommand().
3. 直接結束MainActivity, MyService2沒有任何異動
4. 結束前觸發stopService(), 則會使MyService2進入onDestroy()執行, 並結束其生命週期


溝通機制

在上述的實驗中, 就已經非常清楚地看到MainActivity透過startService()來與MyService2進行傳遞資料, 並透過Intent來夾帶資料.
在 MainActivity.java 中

public void test2(View view){
    Intent intent = new Intent(this, MyService2.class);
    intent.putExtra("username", "brad");
    startService(intent);
}								
而在MyService2中:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.i("brad", "MyService2:onStartCommand()");
    String username = intent.getStringExtra("username");
    return super.onStartCommand(intent, flags, startId);
}								
								
當Activity執行階段要對Service觸發方法時, 則只需要直接對屬性中的Service物件實體操作即可.


假設在MyService1中:

public void doSomething(){
    Log.i("brad", "doSomething...");
}
								
而在MainActivity中:

public void test1(View view){
    mService.doSomething();
}
								
反過來, 要從MyService2發出資料給MainActivity, 則可以透過BroadcastReceiver的機制來處理.
假設在MyService中:

public void doSomething(){
    Log.i("brad", "doSomething...");
    Intent intent = new Intent("brad");
    intent.putExtra("rand", (int)(Math.random()*49+1));
    sendBroadcast(intent);
}
								
回到MainActivity中, 先定義一個自訂內部類別, 繼承自BroadcastReceiver類別, 並Override其onReceive()方法.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // ...								
    myReceiver = new MyReceiver();
    filter = new IntentFilter();
    filter.addAction("brad");
    // ...
}					
//...
private class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("brad", "receive from MyService1");
        int rand = intent.getIntExtra("rand", -1);
        mesg.setText("" + rand);
    }
}								
								
而分別在onStart()及onStop()進行註冊/解除.

    @Override
    protected void onStart() {
        super.onStart();
        registerReceiver(myReceiver, filter);
    }


    @Override
    protected void onStop() {
        super.onPause();
        unregisterReceiver(myReceiver);
    }
								


影片教學