`
k_lb
  • 浏览: 801931 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
社区版块
存档分类
最新评论
  • kitleer: 据我所知,国内有款ETL调度监控工具TaskCTL,支持ket ...
    kettle调度

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

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播放操作。启动后界面如下图:

图中的”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对象进行通信。
启动后的界面如下图:

通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序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 };


启动后的界面如下




分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics