一、bindService簡介
bindService是綁定Service服務,執行service服務中的邏輯流程。
service通過Context.startService()方法開始,通過Context.stopService()方法停止;也可以通過Service.stopSelf()方法或者Service.stopSelfResult()方法來停止自己。只要調用一次stopService()方法便可以停止服務,無論之前它被調用了多少次的啓動服務方法。
客戶端建立一個與Service的連接,並使用此連接與Service進行通話,通過Context.bindService()方法來綁定服務,Context.unbindService()方法來關閉服務。多個客戶端可以綁定同一個服務,如果Service還未被啓動,bindService()方法可以啓動服務。
上面startService()和bindService()兩種模式是完全獨立的。你可以綁定一個已經通過startService()方法啓動的服務。例如:一個後臺播放音樂服務可以通過startService(intend)對象來播放音樂。可能用戶在播放過程中要執行一些操作比如獲取歌曲的一些信息,此時activity可以通過調用bindServices()方法與Service建立連接。這種情況下,stopServices()方法實際上不會停止服務,直到最後一次綁定關閉。
二、bindService啓動流程
context.bindService() ——> onCreate() ——> onBind() ——> Service running ——> onUnbind() ——> onDestroy() ——> Service stoponBind()將返回給客戶端一個IBind接口實例,IBind允許客戶端回調服務的方法,比如得到Service的實例、運行狀態或其他操作。這個時候把調用者(Context,例如Activity)會和Service綁定在一起,Context退出了,Srevice就會調用onUnbind->onDestroy相應退出。
所以調用bindService的生命週期爲:onCreate --> onBind(只一次,不可多次綁定) --> onUnbind --> onDestory。
在Service每一次的開啓關閉過程中,只有onStart可被多次調用(通過多次startService調用),其他onCreate,onBind,onUnbind,onDestory在一個生命週期中只能被調用一次。詳見:Android Service 服務(一)—— Service
三、bindService生命週期
像一個activity那樣,一個service有些可以用來改變狀態的生命週期方法,但是比activity的方法少,service生命週期方法只有三個public
void onCreate()
void onStart(Intent intent)
void onDestroy()
通過實現這三個生命週期方法,你可以監聽service的兩個嵌套循環的生命週期:
1、整個生命週期
service的整個生命週期是在onCreate()和onDestroy()方法之間。和activity一樣,在onCreate()方法裏初始化,在onDestroy()方法裏釋放資源。例如,一個背景音樂播放服務可以在onCreate()方法裏播放,在onDestroy()方法裏停止。
2、活動的生命週期
service的活動生命週期是在onStart()之後,這個方法會處理通過startServices()方法傳遞來的Intent對象。音樂service可以通過開打intent對象來找到要播放的音樂,然後開始後臺播放。注: service停止時沒有相應的回調方法,即沒有onStop()方法,只有onDestroy()銷燬方法。
onCreate()方法和onDestroy()方法是針對所有的services,無論它們是否啓動,通過Context.startService()和Context.bindService()方法都可以訪問執行。然而,只有通過startService()方法啓動service服務時纔會調用onStart()方法。
如果一個service允許別人綁定,那麼需要實現以下額外的方法:
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)
onBind()回調方法會繼續傳遞通過bindService()傳遞來的intent對象
onUnbind()會處理傳遞給unbindService()的intent對象。如果service允許綁定,onBind()會返回客戶端與服務互相聯繫的通信句柄(實例)。
如果建立了一個新的客戶端與服務的連接,onUnbind()方法可以請求調用onRebind()方法。
記住: 任何服務無論它怎樣建立,默認客戶端都可以連接,所以任何service都能夠接收onBind()和onUnbind()方法
四、bindService示例
Activity
- public class PlayBindMusic extends Activity implements OnClickListener {
- private Button playBtn;
- private Button stopBtn;
- private Button pauseBtn;
- private Button exitBtn;
- private BindMusicService musicService;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.bind_music_service);
- playBtn = (Button) findViewById(R.id.play);
- stopBtn = (Button) findViewById(R.id.stop);
- pauseBtn = (Button) findViewById(R.id.pause);
- exitBtn = (Button) findViewById(R.id.exit);
- playBtn.setOnClickListener(this);
- stopBtn.setOnClickListener(this);
- pauseBtn.setOnClickListener(this);
- exitBtn.setOnClickListener(this);
- connection();
- }
- private void connection() {
- Intent intent = new Intent("com.homer.bind.bindService");
- bindService(intent, sc, Context.BIND_AUTO_CREATE); // bindService
- }
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.play:
- musicService.play();
- break;
- case R.id.stop:
- if (musicService != null) {
- musicService.stop();
- }
- break;
- case R.id.pause:
- if (musicService != null) {
- musicService.pause();
- }
- break;
- case R.id.exit:
- this.finish();
- break;
- }
- }
- private ServiceConnection sc = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) { //connect Service
- musicService = ((BindMusicService.MyBinder) (service)).getService();
- if (musicService != null) {
- musicService.play(); // play music
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) { //disconnect Service
- musicService = null;
- }
- };
- @Override
- public void onDestroy(){
- super.onDestroy();
- if(sc != null){
- unbindService(sc);
- }
- }
- }
public class PlayBindMusic extends Activity implements OnClickListener {
private Button playBtn;
private Button stopBtn;
private Button pauseBtn;
private Button exitBtn;
private BindMusicService musicService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bind_music_service);
playBtn = (Button) findViewById(R.id.play);
stopBtn = (Button) findViewById(R.id.stop);
pauseBtn = (Button) findViewById(R.id.pause);
exitBtn = (Button) findViewById(R.id.exit);
playBtn.setOnClickListener(this);
stopBtn.setOnClickListener(this);
pauseBtn.setOnClickListener(this);
exitBtn.setOnClickListener(this);
connection();
}
private void connection() {
Intent intent = new Intent("com.homer.bind.bindService");
bindService(intent, sc, Context.BIND_AUTO_CREATE); // bindService
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.play:
musicService.play();
break;
case R.id.stop:
if (musicService != null) {
musicService.stop();
}
break;
case R.id.pause:
if (musicService != null) {
musicService.pause();
}
break;
case R.id.exit:
this.finish();
break;
}
}
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) { //connect Service
musicService = ((BindMusicService.MyBinder) (service)).getService();
if (musicService != null) {
musicService.play(); // play music
}
}
@Override
public void onServiceDisconnected(ComponentName name) { //disconnect Service
musicService = null;
}
};
@Override
public void onDestroy(){
super.onDestroy();
if(sc != null){
unbindService(sc);
}
}
}
Service
- public class BindMusicService extends Service {
- private MediaPlayer mediaPlayer;
- private final IBinder binder = new MyBinder();
- public class MyBinder extends Binder {
- BindMusicService getService() {
- return BindMusicService.this;
- }
- }
- @Override
- public IBinder onBind(Intent intent) {
- return binder;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT);
- if(mediaPlayer != null){
- mediaPlayer.stop();
- mediaPlayer.release();
- }
- }
- public void play() {
- if (mediaPlayer == null) {
- mediaPlayer = MediaPlayer.create(this, R.raw.tmp);
- mediaPlayer.setLooping(false);
- }
- if (!mediaPlayer.isPlaying()) {
- mediaPlayer.start();
- }
- }
- public void pause() {
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- mediaPlayer.pause();
- }
- }
- public void stop() {
- if (mediaPlayer != null) {
- mediaPlayer.stop();
- try {
- mediaPlayer.prepare(); // 在調用stop後如果需要再次通過start進行播放,需要之前調用prepare函數
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- }
- }
public class BindMusicService extends Service {
private MediaPlayer mediaPlayer;
private final IBinder binder = new MyBinder();
public class MyBinder extends Binder {
BindMusicService getService() {
return BindMusicService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT);
if(mediaPlayer != null){
mediaPlayer.stop();
mediaPlayer.release();
}
}
public void play() {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.tmp);
mediaPlayer.setLooping(false);
}
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
public void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
public void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
try {
mediaPlayer.prepare(); // 在調用stop後如果需要再次通過start進行播放,需要之前調用prepare函數
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
AndroidManifest.xml
- <service
- android:name=".bind.BindMusicService"
- android:enabled="true" >
- <intent-filter>
- <action android:name="com.homer.bind.bindService" />
- </intent-filter>
- </service>
<service
android:name=".bind.BindMusicService"
android:enabled="true" >
<intent-filter>
<action android:name="com.homer.bind.bindService" />
</intent-filter>
</service>
五、代碼解析
1、 Activity中,Intent intent = new Intent("com.homer.bind.bindService"); 構建一個service的action,然後bindService(intent, sc, Context.BIND_AUTO_CREATE); 綁定服務
2、 Activity中,通過private ServiceConnection sc = new ServiceConnection() 建立一個Service連接,onServiceConnected()獲取Service實例,onServiceDisconnected()釋放連接
3、 Service中,重載onBind(Intent intent)方法,返回Service實例(即BindMusicService)給Activity,然後執行onCreate()函數(注:bindService不執行onStart()函數)
4、 Activity中,通過返回的Service實例musicService,執行音樂播放的操作(play、pause、stop等)
六、Remote Service拓展
通常每個應用程序都在它自己的進程內運行,但有時需要在進程之間傳遞對象(IPC通信),你可以通過應用程序UI的方式寫個運行在一個不同的進程中的service。在android平臺中,一個進程通常不能訪問其它進程中的內存區域。所以,他們需要把對象拆分成操作系統能理解的簡單形式,以便僞裝成對象跨越邊界訪問。編寫這種僞裝代碼相當的枯燥乏味,好在android爲我們提供了AIDL工具可以來做這件事。
AIDL(android接口描述語言)是一個IDL語言,它可以生成一段代碼,可以使在一個android設備上運行的兩個進程使用內部通信進程進行交互。如果你需要在一個進程中(例如在一個Activity中)訪問另一個進程中(例如一個Service)某個對象的方法,你就可以使用AIDL來生成這樣的代碼來僞裝傳遞各種參數。
要使用AIDL,Service需要以aidl文件的方式提供服務接口,AIDL工具將生成一個相應的java接口,並且在生成的服務接口中包含一個功能調用的stub服務樁類。Service的實現類需要去繼承這個stub服務樁類。Service的onBind方法會返回實現類的對象,之後你就可以使用它了,參見下例:
IMusicControlService.aidl
- package com.homer.remote;
- interface IMusicControlService{
- void play();
- void stop();
- void pause();
- }
package com.homer.remote;
interface IMusicControlService{
void play();
void stop();
void pause();
}
- public class RemoteMusicService extends Service {
- private MediaPlayer mediaPlayer;
- @Override
- public IBinder onBind(Intent intent) {
- return binder;
- }
- private final IMusicControlService.Stub binder = new IMusicControlService.Stub() {
- @Override
- public void play() throws RemoteException {
- if (mediaPlayer == null) {
- mediaPlayer = MediaPlayer.create(RemoteMusicService.this, R.raw.tmp);
- mediaPlayer.setLooping(false);
- }
- if (!mediaPlayer.isPlaying()) {
- mediaPlayer.start();
- }
- }
- @Override
- public void pause() throws RemoteException {
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- mediaPlayer.pause();
- }
- }
- @Override
- public void stop() throws RemoteException {
- if (mediaPlayer != null) {
- mediaPlayer.stop();
- try {
- mediaPlayer.prepare(); // 在調用stop後如果需要再次通過start進行播放,需要之前調用prepare函數
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- }
- };
- @Override
- public void onDestroy() {
- super.onDestroy();
- if(mediaPlayer != null){
- mediaPlayer.stop();
- mediaPlayer.release();
- }
- }
- }
public class RemoteMusicService extends Service {
private MediaPlayer mediaPlayer;
@Override
public IBinder onBind(Intent intent) {
return binder;
}
private final IMusicControlService.Stub binder = new IMusicControlService.Stub() {
@Override
public void play() throws RemoteException {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(RemoteMusicService.this, R.raw.tmp);
mediaPlayer.setLooping(false);
}
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
@Override
public void pause() throws RemoteException {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
@Override
public void stop() throws RemoteException {
if (mediaPlayer != null) {
mediaPlayer.stop();
try {
mediaPlayer.prepare(); // 在調用stop後如果需要再次通過start進行播放,需要之前調用prepare函數
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
};
@Override
public void onDestroy() {
super.onDestroy();
if(mediaPlayer != null){
mediaPlayer.stop();
mediaPlayer.release();
}
}
}
客戶端(Activity)應用連接到這個Service時,onServiceConnected方法將被調用,客戶端就可以獲得IBinder對象。參看下面的客戶端onServiceConnected方法:
Activity
- public class PlayRemoteMusic extends Activity implements OnClickListener {
- private Button playBtn;
- private Button stopBtn;
- private Button pauseBtn;
- private Button exitBtn;
- private IMusicControlService musicService;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.remote_music_service);
- playBtn = (Button) findViewById(R.id.play);
- stopBtn = (Button) findViewById(R.id.stop);
- pauseBtn = (Button) findViewById(R.id.pause);
- exitBtn = (Button) findViewById(R.id.exit);
- playBtn.setOnClickListener(this);
- stopBtn.setOnClickListener(this);
- pauseBtn.setOnClickListener(this);
- exitBtn.setOnClickListener(this);
- connection();
- }
- private void connection() {
- Intent intent = new Intent("com.homer.remote.remoteMusicReceiver");
- bindService(intent, sc, Context.BIND_AUTO_CREATE); // bindService
- }
- @Override
- public void onClick(View v) {
- try {
- switch (v.getId()) {
- case R.id.play:
- musicService.play();
- break;
- case R.id.stop:
- if (musicService != null) {
- musicService.stop();
- }
- break;
- case R.id.pause:
- if (musicService != null) {
- musicService.pause();
- }
- break;
- case R.id.exit:
- this.finish();
- break;
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- private ServiceConnection sc = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) { //connect Service
- musicService = IMusicControlService.Stub.asInterface(service);
- }
- @Override
- public void onServiceDisconnected(ComponentName name) { //disconnect Service
- musicService = null;
- }
- };
- @Override
- public void onDestroy(){
- super.onDestroy();
- if(sc != null){
- unbindService(sc); // unBindService
- }
- }
- }
public class PlayRemoteMusic extends Activity implements OnClickListener {
private Button playBtn;
private Button stopBtn;
private Button pauseBtn;
private Button exitBtn;
private IMusicControlService musicService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_music_service);
playBtn = (Button) findViewById(R.id.play);
stopBtn = (Button) findViewById(R.id.stop);
pauseBtn = (Button) findViewById(R.id.pause);
exitBtn = (Button) findViewById(R.id.exit);
playBtn.setOnClickListener(this);
stopBtn.setOnClickListener(this);
pauseBtn.setOnClickListener(this);
exitBtn.setOnClickListener(this);
connection();
}
private void connection() {
Intent intent = new Intent("com.homer.remote.remoteMusicReceiver");
bindService(intent, sc, Context.BIND_AUTO_CREATE); // bindService
}
@Override
public void onClick(View v) {
try {
switch (v.getId()) {
case R.id.play:
musicService.play();
break;
case R.id.stop:
if (musicService != null) {
musicService.stop();
}
break;
case R.id.pause:
if (musicService != null) {
musicService.pause();
}
break;
case R.id.exit:
this.finish();
break;
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) { //connect Service
musicService = IMusicControlService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) { //disconnect Service
musicService = null;
}
};
@Override
public void onDestroy(){
super.onDestroy();
if(sc != null){
unbindService(sc); // unBindService
}
}
}
Remote Service流程總結:
1、 Activity(客戶端)中,Intent intent = new Intent("com.homer.remote.remoteMusicReceiver");構建intent,然後bindService(intent, sc, Context.BIND_AUTO_CREATE);綁定服務
2、 Activity(客戶端)中,通過ServiceConnection()重載onServiceConnected()建立連接,獲取Service.Stub實例;onServiceDisconnected()釋放連接(與bindService類似)
3、 Service中,通過重載onBind(Intent intent) 返回Service.Stub實例,但Service.Stub類是由aidl文件生成的接口類中的一個內部類Stub類,Service來繼承該Stub類
4、 Activity中,通過操作Service實例(musicService),執行音樂播放操作(play、pause、stop等)
參考推薦:
Service (android developer)
Android Service 服務(一)—— Service