Android – Service – 啟動型

Service 本身就是處於背景中執行的 Context, 與在前景執行的 Context 不一樣. 前景是負責處理 UI/UX, 而背景中執行的任務, 通常是與 UI/UX 無關的項目, 這樣一來,  就不會影響到使用者的使用體驗, 也能同步在運作在背景中.

啟動型的 Service, 就是可以被 Context 物件實體, 以 startService() 的方式啟動 Service 物件的生命週期. 換言之, 可以由前景的 Activity, 或是另一個已經在背景執行的 Service 物件實體來啟動的 Service.

一但開始執行的 Service 物件實體及其生命週期的表現, 並不會因為啟動的 Activity 的生命週期改變而有所影響. 例如: 當啟動 Service 的 Activity 生命週期結束, 並不會導致 Service 也結束生命週期.

Start Services


啟動機制模式的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來觸發方法:

  • test2(): 呼叫startService()
  • test3(): 呼叫stopService()
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可以清楚的觀察到:

  • 首次觸發startService(), 會建立MyService2物件實體, 直接依序執行onCreate()及onStartCommand()
  • 之後再觸發startService(), 則只會觸發startCommand().
  • 直接結束MainActivity, MyService2沒有任何異動
  • 結束前觸發stopService(), 則會使MyService2進入onDestroy()執行, 並結束其生命週期

 

溝通機制


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

MainActivity中

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()方法.

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()進行註冊/解除.

private MyReceiver myReceiver;
……
    @Override
    protected void onStart() {
        super.onStart();

        Log.i("brad", "Activity:start()");
        Intent intent = new Intent(this, MyService1.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

        myReceiver = new MyReceiver();
        IntentFilter filter = new IntentFilter("brad");
        registerReceiver(myReceiver, filter);

    }
……
    @Override
    protected void onStop() {
        super.onStop();
        Log.i("brad", "Activity:stop()");

        unregisterReceiver(myReceiver);

        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

 

 

%d bloggers like this: