欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android组件之Service  

程序员文章站 2022-06-09 22:42:17
...
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进行远程调用。

下面是整个应用启动界面:android组件之Service
            
    
    
         
先从使用startService启动Service学起


首先编写一个Activity

01 public class PlayMusic extendsActivity implementsOnClickListener {
02 privatestatic final String TAG = "PlayMusic";
03 privateButton playBtn;
04 privateButton stopBtn;
05 privateButton pauseBtn;
06 privateButton exitBtn;
07 privateButton closeBtn;
08
09 //....(详见源码)
10
11 @Override
12 publicvoid onClick(View v) {
13 intop = -1;
14 Intent intent =new Intent("org.allin.android.musicService");
15
16 //广播用
17 // Intent intent = new Intent("org.allin.android.musicReceiver");
18
19 switch(v.getId()) {
20 caseR.id.play:
21 Log.d(TAG,"onClick: playing muic");
22 op =1;
23 break;
24 caseR.id.stop:
25 Log.d(TAG,"onClick: stoping music");
26 op =2;
27 break;
28 caseR.id.pause:
29 Log.d(TAG,"onClick: pausing music");
30 op =3;
31 break;
32 caseR.id.close:
33 Log.d(TAG,"onClick: close");
34 this.finish();
35 break;
36 caseR.id.exit:
37 Log.d(TAG,"onClick: exit");
38 op =4;
39 stopService(intent);
40 this.finish();
41 break;
42 }
43
44 Bundle bundle =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 ,ntent intent = new Intent("org.allin.android.musicService");
"org.allin.android.musicService"是在AndroidManifest.xml文件中对service类的定义

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

把操作码放在Bundle中
Bundle bundle = new Bundle();
bundle.putInt("op", op);
intent.putExtras(bundle);
最后使用startService(intent);启动服务。
下面看看Service是怎么实现的。

MusicService.java

01 /**
02 * @author allin.dev
04 *
05 */
06 public class MusicService extendsService {
07
08 privatestatic final String TAG = "MyService";
09 privateMediaPlayer mediaPlayer;
10
11 /*
12 * (non-Javadoc)
13 *
14 * @see android.app.Service#onBind(android.content.Intent)
15 */
16 @Override
17 publicIBinder onBind(Intent arg0) {
18 returnnull;
19 }
20
21 @Override
22 publicvoid 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 publicvoid onDestroy() {
32 Log.v(TAG,"onDestroy");
33 if(mediaPlayer != null) {
34 mediaPlayer.stop();
35 mediaPlayer.release();
36 }
37 }
38
39 @Override
40 publicvoid onStart(Intent intent,int startId) {
41 Log.v(TAG,"onStart");
42 if(intent != null) {
43 Bundle bundle = intent.getExtras();
44 if(bundle != null) {
45
46 intop = bundle.getInt("op");
47 switch(op) {
48 case1:
49 play();
50 break;
51 case2:
52 stop();
53 break;
54 case3:
55 pause();
56 break;
57 }
58
59 }
60 }
61
62 }
63
64 publicvoid play() {
65 if(!mediaPlayer.isPlaying()) {
66 mediaPlayer.start();
67 }
68 }
69
70 publicvoid pause() {
71 if(mediaPlayer != null&& mediaPlayer.isPlaying()) {
72 mediaPlayer.pause();
73 }
74 }
75
76 publicvoid stop() {
77 if(mediaPlayer != null) {
78 mediaPlayer.stop();
79 try{
80 // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
81 mediaPlayer.prepare();
82 }catch (IOException ex) {
83 ex.printStackTrace();
84 }
85 }
86 }
87
88 }


服务使用了系统自带MediaPlayer进行音乐的播放控制。当调用了startService后服务会先调用onCreate,我们在里面对MediaPlayer进行初始化。接着会调用onStart,可以看到传递给startService()的Intent对象会传递给onStart()方法,这样我们就可以得到intent里面的操作码:
Iundle bundle = intent.getExtras();
int op = bundle.getInt("op");
然后更具定义好的操作码进行相应的f播放操作。启动后界面如下图:
android组件之Service
            
    
    
         
图中的”close”和“exit”是不同的,close只是调用finish()退出当前的Activity,但是Service并没有关掉,音乐会继续播放。而exit就是调用了stopService(intent);来停止服务,Service会调用onDestroy()方法来对mediaPlayer进行停止和释放资源。

有时候如果服务只提供一些操作接口,我们也可以通过广播的g方式来启动服务。
首先要定义一个Receiver,并继承BroadcastReceiver,然后在AndroidManifest.xml中进行注册:
1 <receiver android:name=".MusicReceiver">
2 <intent-filter>
3 <action android:name="org.allin.android.musicReceiver"/>
4 </intent-filter>
5 </receiver>

Receiver的实现:

MusicReceiver.java
01 /**
02 * @author allin.dev
04 *
05 */
06 public class MusicReceiver extendsBroadcastReceiver {
07
08 privatestatic final String TAG = "MusicReceiver";
09 @Override
10 publicvoid onReceive(Context context, Intent intent) {
11 Log.d(TAG,"onReceive");
12 Intent it =new Intent("org.allin.android.musicService");
13 Bundle bundle = intent.getExtras();
14 it.putExtras(bundle);
15
16 if(bundle !=null){
17 intop = 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
Intent intent = new Intent("org.allin.android.musicReceiver");
intent中绑定的操作码都不变,再调用sendBroadcast(intent);把intentg广播出去。
MusicReceiver接受到广播后根据操作码进行相应的操作。




接下来的例子就是使用bindService来启动Service
首先一样是写一个Activity
01 public class PlayBindMusic extendsActivity implementsOnClickListener {
02
03 privatestatic final String TAG = "PlayBindMusic";
04 privateButton playBtn;
05 privateButton stopBtn;
06 privateButton pauseBtn;
07 privateButton exitBtn;
08
09 privateBindMusicService musicService;
10
11 @Override
12 publicvoid onClick(View v) {
13
14 switch(v.getId()) {
15 caseR.id.play:
16 Log.d(TAG,"onClick: binding srvice");
17 musicService.play();
18 break;
19 caseR.id.stop:
20 Log.d(TAG,"onClick: stoping srvice");
21 if(musicService !=null){
22 musicService.stop();
23 }
24 break;
25 caseR.id.pause:
26 Log.d(TAG,"onClick: pausing srvice");
27 if(musicService !=null){
28 musicService.pause();
29 }
30 break;
31 caseR.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 Intent intent =new Intent("org.allin.android.bindService");
42 bindService(intent, sc, Context.BIND_AUTO_CREATE);
43
44 }
45 private ServiceConnection sc = new ServiceConnection() {
46 @Override
47 publicvoid onServiceDisconnected(ComponentName name) {
48 musicService =null;
49 Log.d(TAG,"in onServiceDisconnected");
50 }
51
52 @Override
53 publicvoid onServiceConnected(ComponentName name, IBinder service) {
54 musicService = ((BindMusicService.MyBinder)(service)).getService();
55 if(musicService !=null){
56 musicService.play();
57 }
58
59 Log.d(TAG,"in onServiceConnected");
60 }
61 };
62 }

这里使用了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的实现:

01 /**
02 * @author allin.dev
04 */
05 public class BindMusicService extendsService {
06
07 privatestatic final String TAG = "MyService";
08 privateMediaPlayer mediaPlayer;
09
10 privatefinal IBinder binder =new MyBinder();
11
12 publicclass MyBinder extends Binder {
13 BindMusicService getService() {
14 returnBindMusicService.this;
15 }
16 }
17
18 /*
19 * (non-Javadoc)
20 *
21 * @see android.app.Service#onBind(android.content.Intent)
22 */
23 @Override
24 publicIBinder onBind(Intent intent) {
25 Log.d(TAG,"onBind");
26 play();
27 returnbinder;
28 }
29
30 @Override
31 publicvoid onCreate() {
32 super.onCreate();
33
34 Log.d(TAG,"onCreate");
35 Toast.makeText(this,"show media player", Toast.LENGTH_SHORT).show();
36
37
38 }
39
40 @Override
41 publicvoid onDestroy() {
42 super.onDestroy();
43
44 Log.d(TAG,"onDestroy");
45 Toast.makeText(this,"stop media player", Toast.LENGTH_SHORT);
46 if(mediaPlayer !=null){
47 mediaPlayer.stop();
48 mediaPlayer.release();
49 }
50 }
51
52
53 publicvoid 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 publicvoid pause() {
64 if(mediaPlayer != null&& mediaPlayer.isPlaying()) {
65 mediaPlayer.pause();
66 }
67 }
68
69 publicvoid stop() {
70 if(mediaPlayer != null) {
71 mediaPlayer.stop();
72 try{
73 // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
74 mediaPlayer.prepare();
75 }catch (IOException ex) {
76 ex.printStackTrace();
77 }
78 }
79 }
80
81 }



我们看到Service中有个返回IBinder对象的onBind方法,这个方法会在Service被绑定到其他程序上时被调用,而这个IBinder对象和之前看到的onServiceConnected方法中传入的那个IBinder是同一个东西。应用和Service间就依靠这个IBinder对象进行通信。
启动后的界面如下图:android组件之Service
            
    
    
         

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

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

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


先创建一个IMyRemoteService.aidl文件

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类:

01 /**
02 * @author allin.dev
04 *
05 */
06 public class RemoteMusicService extends Service {
07
08 private static final String TAG = "RemoteMusicService";
09 private MediaPlayer mediaPlayer;
10
11 /*
12 * (non-Javadoc)
13 *
14 * @see android.app.Service#onBind(android.content.Intent)
15 */
16 @Override
17 public IBinder onBind(Intent intent) {
18 return binder;
19 }
20
21 private final IMusicControlServic<SCRIPT type=text/javascript><!--mce:1--></SCRIPT>e.Stub binder = 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 (IOException ex) {
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方法:

01 private ServiceConnection sc = new ServiceConnection() {
02
03 @Override
04 public void onServiceDisconnected(ComponentName name) {
05 musicService = null;
06 Log.d(TAG, "in onServiceDisconnected");
07 }
08
09 @Override
10 public void onServiceConnected(ComponentName name, IBinder service) {
11 musicService = IMusicControlService.Stub.asInterface(service);
12 Log.d(TAG, "in onServiceConnected");
13 }
14 };


启动后的界面如下

android组件之Service