一、bindService簡介
bindService是綁定Service服務(wù),執(zhí)行service服務(wù)中的邏輯流程。
service 通過Context.startService()方法開始,通過Context.stopService()方法停止;也可以通過 Service.stopSelf()方法或者Service.stopSelfResult()方法來停止自己。只要調(diào)用一次 stopService()方法便可以停止服務(wù),無論之前它被調(diào)用了多少次的啟動(dòng)服務(wù)方法。
客戶端建立一個(gè)與 Service的鏈接,并使用此鏈接與Service進(jìn)行通話,通過Context.bindService()方法來綁定服 務(wù),Context.unbindService()方法來關(guān)閉服務(wù)。多個(gè)客戶端可以綁定同一個(gè)服務(wù),如果Service還未被啟 動(dòng),bindService()方法可以啟動(dòng)服務(wù)。
上面startService()和bindService()兩種模 式是完全獨(dú)立的。你可以綁定一個(gè)已經(jīng)通過startService()方法啟動(dòng)的服務(wù)。例如:一個(gè)后臺播放音樂服務(wù)可以通過 startService(intend)對象來播放音樂。可能用戶在播放過程中要執(zhí)行一些操作比如獲取歌曲的一些信息,此時(shí)activity可以通過調(diào) 用bindServices()方法與Service建立連接。這種情況下,stopServices()方法實(shí)際上不會停止服務(wù),直到最后一次綁定關(guān) 閉。
二、bindService啟動(dòng)流程
context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop onBind() 將返回給客戶端一個(gè)IBind接口實(shí)例,IBind允許客戶端回調(diào)服務(wù)的方法,比如得到Service的實(shí)例、運(yùn)行狀態(tài)或其他操作。這個(gè)時(shí)候把調(diào)用者 (Context,例如Activity)會和Service綁定在一起,Context退出了,Srevice就會調(diào)用 onUnbind->onDestroy相應(yīng)退出。
所以調(diào)用bindService的生命周期為:onCreate --> onBind(只一次,不可多次綁定) --> onUnbind --> onDestory。
在Service每一次的開啟關(guān)閉過程中,只有onStart可被多次調(diào)用(通過多次startService調(diào)用),其他onCreate,onBind,onUnbind,onDestory在一個(gè)生命周期中只能被調(diào)用一次。詳見:Android Service 服務(wù)(一)—— Service
三、bindService生命周期
像一個(gè)activity那樣,一個(gè)service有些可以用來改變狀態(tài)的生命周期方法,但是比activity的方法少,service生命周期方法只有三個(gè)public
void onCreate()
void onStart(Intent intent)
void onDestroy()
通過實(shí)現(xiàn)這三個(gè)生命周期方法,你可以監(jiān)聽service的兩個(gè)嵌套循環(huán)的生命周期:
1、整個(gè)生命周期
service 的整個(gè)生命周期是在onCreate()和onDestroy()方法之間。和activity一樣,在onCreate()方法里初始化,在 onDestroy()方法里釋放資源。例如,一個(gè)背景音樂播放服務(wù)可以在onCreate()方法里播放,在onDestroy()方法里停止。
2、活動(dòng)的生命周期
service 的活動(dòng)生命周期是在onStart()之后,這個(gè)發(fā)法會處理通過startServices()方法傳遞來的Intent對象。音樂service可以通 過開打intent對象來找到要播放的音樂,然后開始后臺播放。注: service停止時(shí)沒有相應(yīng)的回調(diào)方法,即沒有onStop()方法。
onCreate()方法和onDestroy()方法是針對所有的services,無論它們是否啟動(dòng),通過Context.startService()和Context.bindService()方法都可以訪問執(zhí)行。然而,只有通過startService()方法啟動(dòng)service服務(wù)時(shí)才會調(diào)用onStart()方法。

如果一個(gè)service允許別人綁定,那么需要實(shí)現(xiàn)以下額外的方法:
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)
onBind()回調(diào)方法會繼續(xù)傳遞通過bindService()傳遞來的intent對像
onUnbind()會處理傳遞給unbindService()的intent對象。如果service允許綁定,onBind()會返回客戶端與服務(wù)互相聯(lián)系的通信句柄(實(shí)例)。
如果建立了一個(gè)新的客戶端與服務(wù)的鏈接,onUnbind()方法可以請求調(diào)用onRebind()方法。
記住任何服務(wù),無論它怎樣建立,都默認(rèn)客戶端可以鏈接,所以任何的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);
- }
- }
- }
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(); // 在調(diào)用stop后如果需要再次通過start進(jìn)行播放,需要之前調(diào)用prepare函數(shù)
- } 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>
五、代碼解析
1、 Activity中,Intent intent = new Intent("com.homer.bind.bindService"); 構(gòu)建一個(gè)service的action,然后bindService(intent, sc, Context.BIND_AUTO_CREATE); 綁定服務(wù)
2、 Activity中,通過private ServiceConnection sc = new ServiceConnection() 建立一個(gè)Service連接,onServiceConnected()獲取Service實(shí)例,onServiceDisconnected()釋放連 接
3、 Service中,重載onBind(Intent intent)方法,返回Service實(shí)例(即BindMusicService)給Activity,然后執(zhí)行onCreate()函數(shù)(注:bindService不執(zhí)行onStart()函數(shù))
4、 Activity中,通過返回的Service實(shí)例musicService,執(zhí)行音樂播放的操作(play、pause、stop等)
六、Remote Service拓展
通 常每個(gè)應(yīng)用程序都在它自己的進(jìn)程內(nèi)運(yùn)行,但有時(shí)需要在進(jìn)程之間傳遞對象(IPC通信),你可以通過應(yīng)用程序UI的方式寫個(gè)運(yùn)行在一個(gè)不同的進(jìn)程中的 service。在android平臺中,一個(gè)進(jìn)程通常不能訪問其它進(jìn)程中的內(nèi)存區(qū)域。所以,他們需要把對象拆分成操作系統(tǒng)能理解的簡單形式,以便偽裝成 對象跨越邊界訪問。編寫這種偽裝代碼相當(dāng)?shù)目菰锓ξ叮迷赼ndroid為我們提供了AIDL工具可以來做這件事。
AIDL(android 接口描述語言)是一個(gè)IDL語言,它可以生成一段代碼,可以使在一個(gè)android設(shè)備上運(yùn)行的兩個(gè)進(jìn)程使用內(nèi)部通信進(jìn)程進(jìn)行交互。如果你需要在一個(gè)進(jìn)程 中(例如在一個(gè)Activity中)訪問另一個(gè)進(jìn)程中(例如一個(gè)Service)某個(gè)對象的方法,你就可以使用AIDL來生成這樣的代碼來偽裝傳遞各種參數(shù)。
要 使用AIDL,Service需要以aidl文件的方式提供服務(wù)接口,AIDL工具將生成一個(gè)相應(yīng)的java接口,并且在生成的服務(wù)接口中包含一個(gè)功能調(diào) 用的stub服務(wù)樁類。Service的實(shí)現(xiàn)類需要去繼承這個(gè)stub服務(wù)樁類。Service的onBind方法會返回實(shí)現(xiàn)類的對象,之后你就可以使用 它了,參見下例:
IMusicControlService.aidl
- package com.homer.remote;
-
- interface IMusicControlService{
- void play();
- void stop();
- void pause();
- }
使用eclipse的Android插件,會根據(jù)這個(gè)aidl文件生成一個(gè)Java接口類,生成的接口類中會有一個(gè)內(nèi)部類Stub類,Service來繼承該Stub類:
Service
- 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(); // 在調(diào)用stop后如果需要再次通過start進(jìn)行播放,需要之前調(diào)用prepare函數(shù)
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- }
- };
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- if(mediaPlayer != null){
- mediaPlayer.stop();
- mediaPlayer.release();
- }
- }
- }
客戶端(Activity)應(yīng)用連接到這個(gè)Service時(shí),onServiceConnected方法將被調(diào)用,客戶端就可以獲得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
- }
- }
- }
Remote Service流程總結(jié):1、 Activity(客戶端)中,Intent intent = new Intent("com.homer.remote.remoteMusicReceiver");構(gòu)建intent,然后 bindService(intent, sc, Context.BIND_AUTO_CREATE);綁定服務(wù)
2、 Activity(客戶端)中,通過ServiceConnection()重載onServiceConnected()建立連接,獲取Service.Stub實(shí)例;onServiceDisconnected()釋放連接(與bindService類似)
3、 Service中,通過重載onBind(Intent intent) 返回Service.Stub實(shí)例,但Service.Stub類是由aidl文件生成的接口類中的一個(gè)內(nèi)部類Stub類,Service來繼承該Stub類
4、 Activity中,通過操作Service實(shí)例(musicService),執(zhí)行音樂播放操作(play、pause、stop等)