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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
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類的定義
1
2
3
4
5
|
<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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
/** * @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中進行註冊:
1
2
3
4
5
|
<receiver android:name= ".MusicReceiver" > <intent-filter> <action android:name= "org.allin.android.musicReceiver"
/> </intent-filter> </receiver> |
Receiver的實現:
MusicReceiver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/** * @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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
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的實現:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
/** * @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對象進行通信。
啓動後的界面如下圖:
[源碼下載]
下一篇《android
service 學習(下) 》會介紹通過AIDL對Service進行遠程調用