Service是android 系统中的一种组件,它跟Activity的级别差不多,但是他不能自己运行,只能后台运行,并且可以和其他组件进行交互。Service的启动有两种方式:context.startService() 和 context.bindService()。 使用context.startService() 启动Service是会会经历:context.startService() ->onCreate()- >onStart()->Service runningcontext.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 runningonUnbind() -> 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
view source print ? 01public class PlayMusic extends Activity implements OnClickListener { 02 private static final String TAG = "PlayMusic"; 03 private Button playBtn; 04 private Button stopBtn; 05 private Button pauseBtn; 06 private Button exitBtn; 07 private Button closeBtn; 08 09 //....(详见源码) 10 11@Override 12 public void onClick(View v) { 13 int op = -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 case R.id.play: 21 Log.d(TAG, "onClick: playing muic"); 22 op = 1; 23 break; 24 case R.id.stop: 25 Log.d(TAG, "onClick: stoping music"); 26 op = 2; 27 break; 28 case R.id.pause: 29 Log.d(TAG, "onClick: pausing music"); 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 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类的定义
view source print ? 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
view source print ? 01/** 02 * @author allin.dev 03 * http://allin.cnblogs.com/ 04 * 05 */ 06public class MusicService extends Service { 07 08 private static final String TAG = "MyService"; 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 arg0) { 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(Intent intent, int startId) { 41 Log.v(TAG, "onStart"); 42 if (intent != null) { 43 Bundle bundle = 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 (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中进行注册:
view source print ? 1<receiver android:name=".MusicReceiver"> 2<intent-filter> 3<action android:name="org.allin.android.musicReceiver" /> 4</intent-filter> 5</receiver>Receiver的实现: MusicReceiver.java
view source print ? 01/** 02 * @author allin.dev 03 * http://allin.cnblogs.com/ 04 * 05 */ 06public class MusicReceiver extends BroadcastReceiver { 07 08 private static final String TAG = "MusicReceiver"; 09 @Override 10 public void 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 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指向ReceiverIntent intent = new Intent("org.allin.android.musicReceiver");intent中绑定的操作码都不变,再调用sendBroadcast(intent);把intentg广播出去。当MusicReceiver接受到广播后根据操作码进行相应的操作。接下来的例子就是使用bindService来启动Service首先一样是写一个Activity
view source print ? 01public class PlayBindMusic extends Activity implements OnClickListener { 02 03 private static final String TAG = "PlayBindMusic"; 04 private Button playBtn; 05 private Button stopBtn; 06 private Button pauseBtn; 07 private Button exitBtn; 08 09 private BindMusicService musicService; 10 11 @Override 12 public void onClick(View v) { 13 14 switch (v.getId()) { 15 case R.id.play: 16 Log.d(TAG, "onClick: binding srvice"); 17 musicService.play(); 18 break; 19 case R.id.stop: 20 Log.d(TAG, "onClick: stoping srvice"); 21 if(musicService != null){ 22 musicService.stop(); 23 } 24 break; 25 case R.id.pause: 26 Log.d(TAG, "onClick: pausing srvice"); 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 39private 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 } 45private ServiceConnection sc = new ServiceConnection() { 46 @Override 47 public void onServiceDisconnected(ComponentName name) { 48 musicService = null; 49 Log.d(TAG, "in onServiceDisconnected"); 50 } 51 52 @Override 53 public void 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实例对象,就可以调用服务提供的各种控制音乐播放的哦功能。
注意要在Activity的onCrate方法中调用connect(),这里顺便说下顺序:
调用connect()函数后就绑定服务,触发Service的onCreate方法,然后调用Service的onBind()方法,绑定完成后又回到Activity的onServiceConnected()方法,在这个方法中的参数IBinder server就是onBind()中返回的,此时我们就可以利用这个IBinder来操作service了。下面看看BindMusicService.java的实现:
view source print ? 01/** 02 * @author allin.dev 03 * http://allin.cnblogs.com/ 04 */ 05public class BindMusicService extends Service { 06 07 private static final String TAG = "MyService"; 08 private MediaPlayer mediaPlayer; 09 10 private final IBinder binder = new MyBinder(); 11 12 public class MyBinder extends Binder { 13 BindMusicService getService() { 14 return BindMusicService.this; 15 } 16 } 17 18 /* 19 * (non-Javadoc) 20 * 21 * @see android.app.Service#onBind(android.content.Intent) 22 */ 23 @Override 24 public IBinder onBind(Intent intent) { 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, "show media player", 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, "stop media player", 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 (IOException ex) { 76 ex.printStackTrace(); 77 } 78 } 79 } 80 81}
我们看到Service中有个返回IBinder对象的onBind方法,这个方法会在Service被绑定到其他程序上时被调用,而这个IBinder对象和之前看到的onServiceConnected方法中传入的那个IBinder是同一个东西。应用和Service间就依靠这个IBinder对象进行通信。启动后的界面如下图:[源码下载]