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來啟動本地的服務。
而在下一篇《
先從使用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物件進行通訊。 啟動後的介面如下圖:
[原始碼下載]