綁定型Service

綁定型 Service 通常適用於跟隨特定的 Activity. 其在背景執行的任務, 會相依於特定的前景的 Activity 的運作, 一旦前景的 Activity 的特定狀態下, 可以解除背景中的 Service.

以播放音樂的任務來說, 如果這是一個播放音樂的應用程式, 通常當使用者離開前景 Activity 下, 也能持續播放. 但是, 如果這是一個遊戲應用程式, 播放音樂是用來播放背景音樂, 所以, 當應用程式結束或是進入暫停狀態時, 應該也要結束或是暫停播放背景音樂. 雖然都是播放音樂的任務, 但是應用場合不一樣, 所使用的 Service 架構也不一樣.

如果想要 Service 提供繫結的功能,並允許其它應用程式元進行繫結,Service 的 onBind() 方法請勿回傳 null ,回傳 IBinder 物件,此物件定義了程式設計介面,用戶端使用此介面來與Service 進行互動。

綁定模式的Service, 通常運用上會搭配Activity來進行處理, 這也就是Bind的主要意義. 當Activity負責UI/UX的任務同時, 背景中存在有Bound Service來等候服務的請求, 這樣的請求任務並非需要高度的優先權, 也可以在執行完畢之後回傳給前景. 而一旦前景的Activity結束生命週期的同時, 也就不在需要使用的Service.

處理方式是用戶端必須實做 ServiceConnection ,當用戶端呼叫 bindService() 與 Service 繫結,也就是與 Service 建立了連線後, 用戶端就能使用 ServiceConnection 來監控兩者間的連線狀況。建立連線後系統會呼叫 ServiceConnection 的 onServiceConnected() 方法來傳遞 IBinder。



開始玩綁定型Service


在Service 中建立一個Binder 的自訂義類並創建它的實體對象,這個類別也是個能夠被用戶端呼叫的公用方法,在此類內建立一個能取得目前 Service 執行個體的公用方法。額外再定義一亂數實體對象,準備給用戶端取得。

建立 Service

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

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class MyService1 extends Service {
    private final IBinder mBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        MyService1 getService() {
            return MyService1.this;
        }
    }

    public MyService1() {
        Log.i("brad", "MyService1()");
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("brad", "MyService:onBind()");
        return mBinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("brad", "MyService:onDestroy()");
    }
}
								
說明:
1. IBinder 為一個Java interface
2. Binder 為一個實作IBinder interface的類別
3. 自訂的LocalBinder類別的物件實體, 將會在onBind()時傳回.
4. LocalBinder物件實體將會是與Service溝通的橋樑

主程序中監控連線

建立一個 Service 的實體對象來做為繫結的Service,與一布林值來判斷此 Service 狀態。

在應用程式元件下的生命週期 onStart()、onStop 加入對 Service 的綁定與結束綁定,當應用程式元件啟動時與 MyService 這個 Service 綁定,bindService() 傳入Intent (欲綁定的Service)、ServiceConnection (監控) 、綁定中的操作 ,而當應用程式元件暫停時如果 myBound 為 true( Service 綁定中 )則結束綁定。

在MainActivity.java中

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
    private MyService1 mService;
    private boolean mBound = false;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            MyService1.LocalBinder binder = (MyService1.LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    @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);
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("brad", "Activity:stop()");
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

}
								
說明:
1. 建立一個ServiceConnection物件實體, 在Activity端用來與Service溝通的物件, 會透過Binder進行處理.
2. 在onStart()中, 透過呼叫bindService(Intent, ServiceConnection, MODE), 繫結一個新的Service物件實體
3. 一但有一個新的Service物件實體時, 將會先執行onCreate(), 再來執行onBind()
4. 並不會執行到onStartCommand(), 因為不需要.
5. 在onStop()中, 透過呼叫unbindService(ServiceConnection)來解除繫結綁定.


溝通機制

在上述的實驗中, 就已經非常清楚地看到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 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;
        }
								
								


影片教學