您的位置:首页 > 移动开发 > Android开发

android组件之Service

2011-11-15 19:57 267 查看
Service是android系统中的一种组件,它跟Activity的级别差不多,但是他不能自己运行,只能后台运行,并且可以和其他组件进行交互。Service的启动有两种方式:context.startService()和context.bindService()。

使用context.startService()
启动Service是会会经历:context.startService()->onCreate()->onStart()->Service
running

context.stopService()|->onDestroy()->Servicestop

如果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()
->Servicestop

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学起

首先编写一个Activityviewsourceprint?

01
public
class
PlayMusic
extends
Activity
implements
OnClickListener{
02
private
static

final
StringTAG=

"PlayMusic"
;
03
private
ButtonplayBtn;
04
private
ButtonstopBtn;
05
private
ButtonpauseBtn;
06
private
ButtonexitBtn;
07
private
ButtoncloseBtn;
08
09
//....(详见源码)
10
11
@Override
12
public
void

onClick(Viewv){
13
int
op=-
1
;
14
Intentintent=
new

Intent(
"org.allin.android.musicService"
);
15
16
//广播用
17
//Intentintent=newIntent("org.allin.android.musicReceiver");
18
19
switch
(v.getId()){
20
case
R.id.play:
21
Log.d(TAG,
"onClick:playingmuic"
);
22
op=
1
;
23
break
;
24
case
R.id.stop:
25
Log.d(TAG,
"onClick:stopingmusic"
);
26
op=
2
;
27
break
;
28
case
R.id.pause:
29
Log.d(TAG,
"onClick:pausingmusic"
);
30
op=
3
;
31
break
;
32
case
R.id.close:
33
Log.d(TAG,
"onClick:close"
);
34
this
.finish();
35
break
;
36
case
R.id.exit:
37
Log.d(TAG,
"onClick:exit"
);
38
op=
4
;
39
stopService(intent);
40
this
.finish();
41
break
;
42
}
43
44
Bundlebundle=
new

Bundle();
45
bundle.putInt(
"op"
,op);
46
intent.putExtras(bundle);
47
startService(intent);
48
49
//sendBroadcast(intent);

50
}

51
52
53
}
通过重写onClick方法来实现对播放音乐的控制。这里把播放音乐的各种操作用数字的方式通过Intent传递给service。

构造一个Intent,ntentintent=newIntent("org.allin.android.musicService");"org.allin.android.musicService"是在AndroidManifest.xml文件中对service类的定义

viewsourceprint?

1
<serviceandroid:enabled=
"true"
android:name=
".MusicService"
>
2
<intent-filter>
3
<actionandroid:name=
"org.allin.android.musicService"
/>
4
</intent-filter>
5
</service>
把操作码放在Bundle中

Bundlebundle=newBundle();bundle.putInt("op",op);intent.putExtras(bundle);最后使用startService(intent);启动服务。

下面看看Service是怎么实现的。

MusicService.java

viewsourceprint?

01
/**
02
*@authorallin.dev
03
*http://allin.cnblogs.com/
04
*
05
*/
06
public
class
MusicService
extends
Service{
07
08
private
static

final
StringTAG=

"MyService"
;
09
private
MediaPlayermediaPlayer;
10
11
/*
12
*(non-Javadoc)
13
*
14
*@seeandroid.app.Service#onBind(android.content.Intent)
15
*/
16
@Override
17
public
IBinderonBind(Intentarg0){
18
return
null
;
19
}

20
21
@Override
22
public
void

onCreate(){
23
Log.v(TAG,
"onCreate"
);
24
if
(mediaPlayer==
null
){
25
mediaPlayer=MediaPlayer.create(
this
,R.raw.tmp);
26
mediaPlayer.setLooping(
false
);
27
}
28
}

29
30
@Override
31
public
void

onDestroy(){
32
Log.v(TAG,
"onDestroy"
);
33
if
(mediaPlayer!=
null
){
34
mediaPlayer.stop();
35
mediaPlayer.release();
36
}
37
}

38
39
@Override
40
public
void

onStart(Intentintent,
int

startId){
41
Log.v(TAG,
"onStart"
);
42
if
(intent!=
null
){
43
Bundlebundle=intent.getExtras();
44
if
(bundle!=
null
){
45
46
int
op=bundle.getInt(
"op"
);
47
switch
(op){
48
case
1
:
49
play();
50
break
;
51
case
2
:
52
stop();
53
break
;
54
case
3
:
55
pause();
56
break
;
57
}
58
59
}
60
}
61
62
}

63
64
public
void

play(){
65
if
(!mediaPlayer.isPlaying()){
66
mediaPlayer.start();
67
}
68
}

69
70
public
void

pause(){
71
if
(mediaPlayer!=
null
&&mediaPlayer.isPlaying()){
72
mediaPlayer.pause();
73
}
74
}

75
76
public
void

stop(){
77
if
(mediaPlayer!=
null
){
78
mediaPlayer.stop();
79
try
{
80
//在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
81
mediaPlayer.prepare();
82
}
catch

(IOExceptionex){
83
ex.printStackTrace();
84
}
85
}
86
}

87
88
}
服务使用了系统自带MediaPlayer进行音乐的播放控制。当调用了startService后服务会先调用onCreate,我们在里面对MediaPlayer进行初始化。接着会调用onStart,可以看到传递给startService()的Intent对象会传递给onStart()方法,这样我们就可以得到intent里面的操作码:

Iundlebundle=intent.getExtras();intop=bundle.getInt("op");然后更具定义好的操作码进行相应的f播放操作。启动后界面如下图:



图中的”close”和“exit”是不同的,close只是调用finish()退出当前的Activity,但是Service并没有关掉,音乐会继续播放。而exit就是调用了stopService(intent);来停止服务,Service会调用onDestroy()方法来对mediaPlayer进行停止和释放资源。

有时候如果服务只提供一些操作接口,我们也可以通过广播的g方式来启动服务。

首先要定义一个Receiver,并继承BroadcastReceiver,然后在AndroidManifest.xml中进行注册:

viewsourceprint?

1
<receiverandroid:name=
".MusicReceiver"
>
2
<intent-filter>
3
<actionandroid:name=
"org.allin.android.musicReceiver"
/>
4
</intent-filter>
5
</receiver>
Receiver的实现:

MusicReceiver.java

viewsourceprint?

01
/**
02
*@authorallin.dev
03
*http://allin.cnblogs.com/
04
*
05
*/
06
public
class
MusicReceiver
extends
BroadcastReceiver{
07
08
private
static

final
StringTAG=

"MusicReceiver"
;
09
@Override
10
public
void

onReceive(Contextcontext,Intentintent){
11
Log.d(TAG,
"onReceive"
);
12
Intentit=
new

Intent(
"org.allin.android.musicService"
);
13
Bundlebundle=intent.getExtras();
14
it.putExtras(bundle);
15
16
if
(bundle!=
null
){
17
int
op=bundle.getInt(
"op"
);
18
if
(op==
4
){
19
context.stopService(it);
20
}
else
{
21
context.startService(it);
22
}
23
}
24
25
}

26
27
}
然后对PlayMusic中的onclick方法进行些改造,把Intent指向Receiver

Intentintent=newIntent("org.allin.android.musicReceiver");intent中绑定的操作码都不变,再调用sendBroadcast(intent);把intentg广播出去。

当MusicReceiver接受到广播后根据操作码进行相应的操作。

接下来的例子就是使用bindService来启动Service

首先一样是写一个Activityviewsourceprint?

01
public
class
PlayBindMusic
extends
Activity
implements
OnClickListener{
02
03
private
static

final
StringTAG=

"PlayBindMusic"
;
04
private
ButtonplayBtn;
05
private
ButtonstopBtn;
06
private
ButtonpauseBtn;
07
private
ButtonexitBtn;
08
09
private
BindMusicServicemusicService;
10
11
@Override
12
public
void

onClick(Viewv){
13
14
switch
(v.getId()){
15
case
R.id.play:
16
Log.d(TAG,
"onClick:bindingsrvice"
);
17
musicService.play();
18
break
;
19
case
R.id.stop:
20
Log.d(TAG,
"onClick:stopingsrvice"
);
21
if
(musicService!=
null
){
22
musicService.stop();
23
}
24
break
;
25
case
R.id.pause:
26
Log.d(TAG,
"onClick:pausingsrvice"
);
27
if
(musicService!=
null
){
28
musicService.pause();
29
}
30
break
;
31
case
R.id.exit:
32
Log.d(TAG,
"onClick:exit"
);
33
this
.finish();
34
break
;
35
}
36
}

37
38
39
private
void
connection(){
40
Log.d(TAG,
"connecting....."
);
41
Intentintent=
new

Intent(
"org.allin.android.bindService"
);
42
bindService(intent,sc,Context.BIND_AUTO_CREATE);
43
44
}

45
private

ServiceConnectionsc=
new

ServiceConnection(){
46
@Override
47
public
void

onServiceDisconnected(ComponentNamename){
48
musicService=
null
;
49
Log.d(TAG,
"inonServiceDisconnected"
);
50
}
51
52
@Override
53
public
void

onServiceConnected(ComponentNamename,IBinderservice){
54
musicService=((BindMusicService.MyBinder)(service)).getService();
55
if
(musicService!=
null
){
56
musicService.play();
57
}
58
59
Log.d(TAG,
"inonServiceConnected"
);
60
}
61
};

62
}
这里使用了bindService(intent,sc,Context.BIND_AUTO_CREATE);来启动服务的,

我们需要定义ServiceConnectionnn,并实现里面的方法,当服务绑定成功后会调用ServiceConnectionnn中的回调函数:

publicvoidonServiceConnected(ComponentNamename,IBinder
service),

回调函数里面使用musicService=((BindMusicService.MyBinder)(service)).getService();来获取BindMusicService服务对象,有了BindMusicService实例对象,就可以调用服务提供的各种控制音乐播放的哦功能。

下面看看BindMusicService.java的实现:

viewsourceprint?

01
/**
02
*@authorallin.dev
03
*http://allin.cnblogs.com/
04
*/
05
public
class
BindMusicService
extends
Service{
06
07
private
static

final
StringTAG=

"MyService"
;
08
private
MediaPlayermediaPlayer;
09
10
private
final

IBinderbinder=
new

MyBinder();
11
12
public
class

MyBinder
extends

Binder{
13
BindMusicServicegetService(){
14
return
BindMusicService.
this
;
15
}
16
}

17
18
/*
19
*(non-Javadoc)
20
*
21
*@seeandroid.app.Service#onBind(android.content.Intent)
22
*/
23
@Override
24
public
IBinderonBind(Intentintent){
25
Log.d(TAG,
"onBind"
);
26
play();
27
return
binder;
28
}

29
30
@Override
31
public
void

onCreate(){
32
super
.onCreate();
33
34
Log.d(TAG,
"onCreate"
);
35
Toast.makeText(
this
,
"showmediaplayer"
,Toast.LENGTH_SHORT).show();
36
37
38
}

39
40
@Override
41
public
void

onDestroy(){
42
super
.onDestroy();
43
44
Log.d(TAG,
"onDestroy"
);
45
Toast.makeText(
this
,
"stopmediaplayer"
,Toast.LENGTH_SHORT);
46
if
(mediaPlayer!=
null
){
47
mediaPlayer.stop();
48
mediaPlayer.release();
49
}
50
}

51
52
53
public
void

play(){
54
if
(mediaPlayer==
null
){
55
mediaPlayer=MediaPlayer.create(
this
,R.raw.tmp);
56
mediaPlayer.setLooping(
false
);
57
}
58
if
(!mediaPlayer.isPlaying()){
59
mediaPlayer.start();
60
}
61
}

62
63
public
void

pause(){
64
if
(mediaPlayer!=
null
&&mediaPlayer.isPlaying()){
65
mediaPlayer.pause();
66
}
67
}

68
69
public
void

stop(){
70
if
(mediaPlayer!=
null
){
71
mediaPlayer.stop();
72
try
{
73
//在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
74
mediaPlayer.prepare();
75
}
catch

(IOExceptionex){
76
ex.printStackTrace();
77
}
78
}
79
}

80
81
}
我们看到Service中有个返回IBinder对象的onBind方法,这个方法会在Service被绑定到其他程序上时被调用,而这个IBinder对象和之前看到的onServiceConnected方法中传入的那个IBinder是同一个东西。应用和Service间就依靠这个IBinder对象进行通信。

启动后的界面如下图:


通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service。在android平台中,一个进程通常不能访问其他进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。编写这种伪装代码相当的枯燥乏味,好在android为我们提供了AIDL工具可以来做这件事。

AIDL(android接口描述语言)是一个IDL语言,它可以生成一段代码,可以使在一个android设备上运行的两个进程使用内部通信进程进行交互。如果你需要在一个进程中(例如:在一个Activity中)访问另一个进程中(例如:一个Service)某个对象的方法,你就可以使用AIDL来生成这样的代码来伪装传递各种参数。

要使用AIDL,Service需要以aidl文件的方式提供服务接口,AIDL工具将生成一个相应的java接口,并且在生成的服务接口中包含一个功能调用的stub服务桩类。Service的实现类需要去继承这个stub服务桩类。Service的onBind方法会返回实现类的对象,之后你就可以使用它了,参见下例:

先创建一个IMyRemoteService.aidl文件

viewsourceprint?

1
package

org.allin.android.remote;
2
interface

IMusicControlService{
3
void

play();
4
void

stop();
5
void

pause();
6
}
如果你正在使用eclipse的Android插件,则它会根据这个aidl文件生成一个Java接口类。生成的接口类中会有一个内部类Stub类,你要做的事就是去继承该Stub类:

viewsourceprint?

01
/**
02
*@authorallin.dev
03
*http://allin.cnblogs.com/
04
*
05
*/
06
public
class
RemoteMusicService
extends

Service{
07
08
private

static
final

StringTAG=
"RemoteMusicService"
;
09
private

MediaPlayermediaPlayer;
10
11
/*
12
*(non-Javadoc)
13
*
14
*@seeandroid.app.Service#onBind(android.content.Intent)
15
*/
16
@Override
17
public

IBinderonBind(Intentintent){
18
return

binder;
19
}

20
21
private

final
IMusicControlServic<SCRIPTtype=text/javascript><!--mce:
1
--></SCRIPT>e.Stubbinder=
new
IMusicControlService.Stub(){
22
23
@Override
24
public

void
stop()
throws

RemoteException{
25
Log.d(TAG,
"stop...."
);
26
if

(mediaPlayer!=
null
){
27
mediaPlayer.stop();
28
try

{
29
//在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
30
mediaPlayer.prepare();
31
}
catch
(IOExceptionex){
32
ex.printStackTrace();
33
}
34
}
35
36
}
37
38
@Override
39
public

void
play()
throws

RemoteException{
40
Log.d(TAG,
"play...."
);
41
if

(mediaPlayer==
null
){
42
mediaPlayer=MediaPlayer.create(RemoteMusicService.
this
,
43
R.raw.tmp);
44
mediaPlayer.setLooping(
false
);
45
}
46
if

(!mediaPlayer.isPlaying()){
47
mediaPlayer.start();
48
}
49
}
50
51
@Override
52
public

void
pause()
throws

RemoteException{
53
Log.d(TAG,
"pause...."
);
54
55
if

(mediaPlayer!=
null

&&mediaPlayer.isPlaying()){
56
mediaPlayer.pause();
57
}
58
}
59
60
};

61
62
63
@Override
64
public

void
onDestroy(){

65
super
.onDestroy();
66
67
Log.d(TAG,
"onDestroy"
);

68
if
(mediaPlayer!=
null
){
69
mediaPlayer.stop();
70
mediaPlayer.release();
71
}
72
}

73
}
当客户端应用连接到这个Service时,onServiceConnected方法将被调用,客户端就可以获得IBinder对象。参看下面的客户端onServiceConnected方法:

viewsourceprint?

01
private

ServiceConnectionsc=
new

ServiceConnection(){
02
03
@Override
04
public

void
onServiceDisconnected(ComponentNamename){
05
musicService=
null
;
06
Log.d(TAG,
"inonServiceDisconnected"
);
07
}
08
09
@Override
10
public

void
onServiceConnected(ComponentNamename,IBinderservice){
11
musicService=IMusicControlService.Stub.asInterface(service);
12
Log.d(TAG,
"inonServiceConnected"
);
13
}
14
};
启动后的界面如下

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: