1. 程式人生 > 其它 >android service 學習(上)

android service 學習(上)

android service 學習(上)

Service是android 系統中的一種元件,它跟Activity的級別差不多,但是他不能自己執行,只能後臺執行,並且可以和其他元件進行互動。Service的啟動有兩種方式:context.startService() 和 context.bindService()。 使用context.startService() 啟動Service是會會經歷: context.startService()  ->onCreate()- >onStart()->Service running context.stopService() | ->onDestroy() ->Service stop 如果Service還沒有執行,則android先呼叫onCreate()然後呼叫onStart();如果Service已經執行,則只調用onStart(),所以一個Service的onStart方法可能會重複呼叫多次。  stopService的時候直接onDestroy,如果是呼叫者自己直接退出而沒有呼叫stopService的話,Service會一直在後臺執行。該Service的呼叫者再啟動起來後可以通過stopService關閉Service。 所以呼叫startService的生命週期為:onCreate --> onStart(可多次呼叫) --> onDestroy 使用使用context.bindService()啟動Service會經歷: context.bindService()->onCreate()->onBind()->Service running onUnbind() -> onDestroy() ->Service stop onBind將返回給客戶端一個IBind介面例項,IBind允許客戶端回撥服務的方法,比如得到Service執行的狀態或其他操作。這個時候把呼叫者(Context,例如Activity)會和Service繫結在一起,Context退出了,Srevice就會呼叫onUnbind->onDestroy相應退出。  所以呼叫bindService的生命週期為:onCreate --> onBind(只一次,不可多次繫結) --> onUnbind --> onDestory。 在Service每一次的開啟關閉過程中,只有onStart可被多次呼叫(通過多次startService呼叫),其他onCreate,onBind,onUnbind,onDestory在一個生命週期中只能被呼叫一次。 service可以在和多場合的應用中使用,比如播放多媒體的時候使用者啟動了其他Activity這個時候程式要在後臺繼續播放,比如檢測SD卡上檔案的變化,再或者在後臺記錄你地理資訊位置的改變等等,總之服務嘛,總是藏在後頭的。 下面我做了一個簡單的音樂播放的應用,分別使用startService和bindService來啟動本地的服務。 而在下一篇《

android service 學習(下) 》會介紹通過AIDL對Service進行遠端呼叫。 下面是整個應用啟動介面:

先從使用startService啟動Service學起 首先編寫一個Activity

public class PlayMusic extends Activity implements OnClickListener {
	private static final String TAG = "PlayMusic";
	private Button playBtn;
	private Button stopBtn;
	private Button pauseBtn;
	private Button exitBtn;
	private Button closeBtn;

	//....(詳見原始碼)

@Override
	public void onClick(View v) {
		int op = -1;
		Intent intent = new Intent("org.allin.android.musicService");
		
		//廣播用
//		Intent intent = new Intent("org.allin.android.musicReceiver");
		
		switch (v.getId()) {
		case R.id.play:
			Log.d(TAG, "onClick: playing muic");
			op = 1;
			break;
		case R.id.stop:
			Log.d(TAG, "onClick: stoping music");
			op = 2;
			break;
		case R.id.pause:
			Log.d(TAG, "onClick: pausing music");
			op = 3;
			break;
		case R.id.close:
			Log.d(TAG, "onClick: close");
			this.finish();
			break;
		case R.id.exit:
			Log.d(TAG, "onClick: exit");
			op = 4;
			stopService(intent);
			this.finish();
			break;
		}
		
		Bundle bundle  = new Bundle();
		bundle.putInt("op", op);
		intent.putExtras(bundle);
		startService(intent);
		
//		sendBroadcast(intent);
	}


}

通過重寫onClick方法來實現對播放音樂的控制。這裡把播放音樂的各種操作用數字的方式通過Intent傳遞給service。  構造一個Intent ,ntent intent = new Intent("org.allin.android.musicService"); "org.allin.android.musicService"是在AndroidManifest.xml檔案中對service類的定義

<service android:enabled="true" android:name=".MusicService">
<intent-filter>
<action android:name="org.allin.android.musicService" />
</intent-filter>
</service>

把操作碼放在Bundle中 Bundle bundle  = new Bundle(); bundle.putInt("op", op); intent.putExtras(bundle); 最後使用startService(intent);啟動服務。 下面看看Service是怎麼實現的。 MusicService.java

/**
 * @author allin.dev
 * http://allin.cnblogs.com/
 * 
 */
public class MusicService extends Service {

	private static final String TAG = "MyService";
	private MediaPlayer mediaPlayer;

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Service#onBind(android.content.Intent)
	 */
	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}

	@Override
	public void onCreate() {
		Log.v(TAG, "onCreate");
		if (mediaPlayer == null) {
			mediaPlayer = MediaPlayer.create(this, R.raw.tmp);
			mediaPlayer.setLooping(false);
		}
	}

	@Override
	public void onDestroy() {
		Log.v(TAG, "onDestroy");
		if (mediaPlayer != null) {
			mediaPlayer.stop();
			mediaPlayer.release();
		}
	}

	@Override
	public void onStart(Intent intent, int startId) {
		Log.v(TAG, "onStart");
		if (intent != null) {
			Bundle bundle = intent.getExtras();
			if (bundle != null) {

				int op = bundle.getInt("op");
				switch (op) {
				case 1:
					play();
					break;
				case 2:
					stop();
					break;
				case 3:
					pause();
					break;
				}

			}
		}

	}

	public void play() {
		if (!mediaPlayer.isPlaying()) {
			mediaPlayer.start();
		}
	}

	public void pause() {
		if (mediaPlayer != null && mediaPlayer.isPlaying()) {
			mediaPlayer.pause();
		}
	}

	public void stop() {
		if (mediaPlayer != null) {
			mediaPlayer.stop();
			try {
				// 在呼叫stop後如果需要再次通過start進行播放,需要之前呼叫prepare函式
				mediaPlayer.prepare();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

}

服務 使用了系統自帶MediaPlayer進行音樂的播放控制。 當呼叫了startService後服務會先呼叫onCreate,我們在裡面對MediaPlayer進行初始化。接著會呼叫onStart,可以看到傳遞給startService()的Intent物件會傳遞給onStart()方法,這樣我們就可以得到intent裡面的操作碼: 

Iundle bundle = intent.getExtras();

int op = bundle.getInt("op");

然後更具定義好的操作碼進行相應的f播放操作。啟動後介面如下圖:

圖中的”close”和“exit”是不同的,close只是呼叫finish()退出當前的Activity,但是Service並沒有關掉,音樂會繼續播放。而exit就是呼叫了stopService(intent);來停止服務,Service會呼叫onDestroy()方法來對mediaPlayer進行停止和釋放資源。

有時候如果服務只提供一些操作介面,我們也可以通過廣播的g方式來啟動服務。

首先要定義一個Receiver,並繼承BroadcastReceiver,然後在AndroidManifest.xml中進行註冊:

<receiver android:name=".MusicReceiver">
<intent-filter>
<action android:name="org.allin.android.musicReceiver" />
</intent-filter>
</receiver>

Receiver的實現:

MusicReceiver.java

/**
 * @author allin.dev
 * http://allin.cnblogs.com/
 *
 */
public class MusicReceiver extends BroadcastReceiver {

	private static final String TAG = "MusicReceiver";
	@Override
	public void onReceive(Context context, Intent intent) {
		Log.d(TAG, "onReceive");
		Intent it = new Intent("org.allin.android.musicService");
		Bundle bundle = intent.getExtras();
		it.putExtras(bundle);
		
		if(bundle != null){
			int op = bundle.getInt("op");
			if(op == 4){
				context.stopService(it);
			}else{
				context.startService(it);
			}
		}
		
	}

}

然後對PlayMusic中的onclick方法進行些改造,把Intent指向Receiver

Intent intent = new Intent("org.allin.android.musicReceiver");

intent中繫結的操作碼都不變,再呼叫sendBroadcast(intent);把intentg廣播出去。

當MusicReceiver接受到廣播後根據操作碼進行相應的操作。

接下來的例子就是使用bindService來啟動Service

首先一樣是寫一個Activity

public class PlayBindMusic extends Activity implements OnClickListener {

	private static final String TAG = "PlayBindMusic";
	private Button playBtn;
	private Button stopBtn;
	private Button pauseBtn;
	private Button exitBtn;
	
	private BindMusicService musicService;

	@Override
	public void onClick(View v) {

		switch (v.getId()) {
		case R.id.play:
			Log.d(TAG, "onClick: binding srvice");
			musicService.play();
			break;
		case R.id.stop:
			Log.d(TAG, "onClick: stoping srvice");
			if(musicService != null){
				musicService.stop();
			}
			break;
		case R.id.pause:
			Log.d(TAG, "onClick: pausing srvice");
			if(musicService != null){
				musicService.pause();
			}
			break;
		case R.id.exit:
			Log.d(TAG, "onClick: exit");
			this.finish();
			break;
		}
	}


private void connection(){
		Log.d(TAG, "connecting.....");
		Intent intent = new Intent("org.allin.android.bindService");
		bindService(intent, sc, Context.BIND_AUTO_CREATE);
		
	}
private ServiceConnection sc = new ServiceConnection() {
		@Override
		public void onServiceDisconnected(ComponentName name) {
			musicService = null;
			Log.d(TAG, "in onServiceDisconnected");
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			musicService = ((BindMusicService.MyBinder)(service)).getService();
			if(musicService != null){
				musicService.play();
			}
			
			Log.d(TAG, "in onServiceConnected");
		}
	};
}

這裡使用了bindService(intent, sc, Context.BIND_AUTO_CREATE);來啟動服務的, 我們需要定義ServiceConnectionnn,並實現裡面的方法,當服務繫結成功後會呼叫ServiceConnectionnn中的回撥函式: public void onServiceConnected(ComponentName name, IBinder service), 回撥函式裡面使用musicService = ((BindMusicService.MyBinder)(service)).getService();來獲取BindMusicService服務物件,有了BindMusicService例項物件,就可以呼叫服務提供的各種控制音樂播放的哦功能。 下面看看BindMusicService.java的實現:

/**
 * @author allin.dev
 * http://allin.cnblogs.com/
 */
public class BindMusicService extends Service {

	private static final String TAG = "MyService";
	private MediaPlayer mediaPlayer;

	private final IBinder binder = new MyBinder();

	public class MyBinder extends Binder {
		BindMusicService getService() {
			return BindMusicService.this;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Service#onBind(android.content.Intent)
	 */
	@Override
	public IBinder onBind(Intent intent) {
		Log.d(TAG, "onBind");
		play();
		return binder;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		
		Log.d(TAG, "onCreate");
		Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();
		
		
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		
		Log.d(TAG, "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 {
				// 在呼叫stop後如果需要再次通過start進行播放,需要之前呼叫prepare函式
				mediaPlayer.prepare();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

}

我們看到Service中有個返回IBinder物件的onBind方法,這個方法會在Service被繫結到其他程式上時被呼叫,而這個IBinder物件和之前看到的onServiceConnected方法中傳入的那個IBinder是同一個東西。應用和Service間就依靠這個IBinder物件進行通訊。 啟動後的介面如下圖:

[原始碼下載]