1、什么是service
Service,看名字就知道跟正常理解的 服务 差不多,后台运行,可交互这样的一个东西。它跟Activity的级别差不多,但是他不能自己运行,需 要通过某一个Activity或者其他Context对象来调用, Context.startService() 和 Context.bindService()。
两种启动Service的方式有所不同。这里要说明一下的是如果你在Service的onCreate或者onStart做一些很耗时间的事情,最好 在Service里启动一个线程来完成,因为Service是跑在主线程中,会影响到你的UI操作或者阻塞主线程中的其他事情。
什么时候需要Service呢?比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的。
Service是在一段不定的时间运行在后台,不和用户交互应用组件。每个Service必须在manifest中 通过<service>来声明。可以通过contect.startservice和contect.bindserverice来启动。
Service和其他的应用组件一样,运行在进程的主线程中。这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现。
service的两种模式(startService()/bindService()不是完全分离的):
1)本地服务 Local Service 用于应用程序内部。
它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用 Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次 stopService()来停止服务。
用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。
2)远程服务 Remote Service 用于android系统内部的应用程序之间。
它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用 Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载 它。
可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。
2、本地服务
2.1 startservice方式
Service的启动方式一:
启动:Context.startService(new Intent(context,xxx.class));
停止:Context.stopService() ;
我画了一个Service启动的流程图,相信大家一看就懂。Activity通过Intent启动Service,如果Service还没有运行,则 android先调用onCreate()然后调用onStart();如果Service已经运行,则只调用onStart(),所以一个 Service的onStart方法可能会重复调用多次。 调用stopService就会触发Service的onDestroy()方法。 图片点击放大~
我们先来介绍一下本地服务的例子。
2.1.1 界面
其中可以播放音乐、暂停、停止等。同时点击finish键可以关闭应用(但服务不关闭,音乐可以继续播放),点击exit推出 应用,同时关闭服务。
2.1.2 音乐播放
/**
* ClassName:MyMediaController
* Function: Mediaplayer 的一个控制类,控制播放器的播放 暂停 停止 等动作
* REASON
*
* @author Leon
* @version
* @since Ver 1.1
* @Date 2011-5-16
*/
public enum MyMediaController implements Serializable {
play {
@Override
public void execute() {
if (mediaPlayer != null && !mediaPlayer.isPlaying())
mediaPlayer.start();
// TODO Auto-generated method stub
}
},
pause {
@Override
public void execute() {
// TODO Auto-generated method stub
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
},
stop {
@Override
public void execute() {
// TODO Auto-generated method stub
if (mediaPlayer != null) {
mediaPlayer.stop();
try {
// 在stop后如果要重新Start需要prepare一下
mediaPlayer.prepare();
// 从头播放
mediaPlayer.seekTo(0);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
public static MediaPlayer mediaPlayer;
public abstract void execute();
}
2.1.3 service
/**
* ClassName:MusicService
* Function: TODO ADD FUNCTION
* Reason:TODO ADD REASON
*
* @authorLeon
* @version
* @sinceVer 1.1
* @Date2011-5-15
*/
public class MusicService extends Service{
privateStringTAG = MusicService.class.getSimpleName();
privateMediaPlayer myMediaPlayer ;
publicstatic finalString INTENT_KEY= "action" ;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
Log.v(TAG , TAG+ " onCreate()");
super.onCreate();
if(myMediaPlayer==null){
myMediaPlayer=MediaPlayer.create(this, R.raw.test) ;
myMediaPlayer.setLooping(false);
}
}
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
Log.v(TAG , TAG + " onStart()");
super.onStart(intent, startId);
if(intent!=null){
MyMediaController mediaControl =(MyMediaController)intent.getSerializableExtra(MusicService.INTENT_KEY);
mediaControl.mediaPlayer=myMediaPlayer;
mediaControl.execute();
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.v(TAG , " onDestroy");
if(myMediaPlayer!=null){
myMediaPlayer.stop();
myMediaPlayer.release();
}
}
}
2.1.4 界面
public class ServiceTestServerActivity extends Activity implements OnClickListener{
/** Called when the activity is first created. */
private static final String TAG = ServiceTestServerActivity.class.getSimpleName();
private TextView m_TextView_text;
private Button btnPlay;
private Button btnPause;
private Button btnStop;
private Button btnFinish;
private Button btnExit;
Intent intent;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
m_TextView_text= (TextView)findViewById(R.id.text);
btnPlay= (Button)findViewById(R.id.play);
btnPause= (Button)findViewById(R.id.pause);
btnStop= (Button)findViewById(R.id.stop);
btnFinish= (Button)findViewById(R.id.finish);
btnExit= (Button)findViewById(R.id.exit);
btnPlay.setOnClickListener(this);
btnPause.setOnClickListener(this);
btnStop.setOnClickListener(this);
btnFinish.setOnClickListener(this);
btnExit.setOnClickListener(this);
intent=new Intent("xuxm.demo.service01.MusicService");
}
private void playAction(MyMediaController playType) {
Bundle bundle = new Bundle();
bundle.putSerializable(MusicService.INTENT_KEY, playType);
intent.putExtras(bundle);
ServiceTestServerActivity.this.startService(intent);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId())
{
case R.id.play:
playAction(MyMediaController.play);
m_TextView_text.setText(R.string.play);
Log.d(TAG, "play.......");
break;
case R.id.pause:
playAction(MyMediaController.pause);
m_TextView_text.setText(R.string.pause);
Log.d(TAG, "pause.......");
break;
case R.id.stop:
playAction(MyMediaController.stop);
m_TextView_text.setText(R.string.stop);
Log.d(TAG, "stop.......");
break;
case R.id.finish:
Log.d(TAG, "close.......");
this.finish();
break;
case R.id.exit:
Log.d(TAG, "exit.......");
stopService(intent);
this.finish();
break;
default:
}
}
}
程序还是蛮简单的,所以不分析了。
运行时可以发现第一次startService时,会调用onCreate和onStart,在没有stopService前,无论点击多少次 startService,都只会调用onStart。而stopService时调用onDestroy。再次点击stopService,会发现不会 进入service的生命周期的,即不会再调用onCreate,onStart和onDestroy。
而onBind在startService/stopService中没有调用。
2.1 bind service方式
第二种是通过绑定的方式来启动Service。先看流程图,点击放大。
在这里我们使用了this.bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);来启动Service,当Service创建了同时绑定了之后,会回调我们定义的ServiceConnection(),从而传回IBinder接 口,我们就能够调用Service中的方法了。这时候Activity就和Service实现了绑定,Activity退出了Service就相应的退出 了。
<service android:name=".MusicService02" >
<intent-filter >
<action android:name="xuxm.demo.service02.MusicService" />
</intent-filter>
</service>
主要代码如下:
MusicService02
public class MusicService02 extends Service{
privateStringTAG = MusicService02.class.getSimpleName();
privateMediaPlayer myMediaPlayer ;
publicstatic finalString INTENT_KEY= "action" ;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
Log.v(TAG, TAG+" onBind");
return mbinder;
}
LocalBinder mbinder=new LocalBinder();
public class LocalBinder extends Binder {
MusicService02 getService() {
return MusicService02.this;
}
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
Log.v(TAG , TAG+ " onCreate()");
super.onCreate();
if(myMediaPlayer==null){
myMediaPlayer=MediaPlayer.create(this, R.raw.test) ;
myMediaPlayer.setLooping(false);
}
}
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
Log.v(TAG , TAG + " onStart()");
super.onStart(intent, startId);
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
Log.v(TAG , TAG +" onUnbind ,成功没有?" + super.onUnbind(intent));
return true;
//return false;
}
@Override
public void onRebind(Intent intent) {
// TODO Auto-generated method stub
Log.v(TAG , TAG +" onRebind()----------------------------------->");
super.onRebind(intent);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.v(TAG , " onDestroy");
if(myMediaPlayer!=null){
myMediaPlayer.stop();
myMediaPlayer.release();
}
}
public MediaPlayer getMyMediaPlayer() {
return myMediaPlayer;
}
public void setMyMediaPlayer(MediaPlayer myMediaPlayer) {
this.myMediaPlayer = myMediaPlayer;
}
}
ServiceTestServerActivity
public class ServiceTestServerActivity extends Activity implements OnClickListener{
/** Called when the activity is first created. */
private static final String TAG = ServiceTestServerActivity.class.getSimpleName();
private TextView m_TextView_text;
private Button btnPlay;
private Button btnPause;
private Button btnStop;
private Button btnUnbind;
private Button btnFinish;
private Button btnExit;
private MusicService02 bindMusicService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
m_TextView_text= (TextView)findViewById(R.id.text);
btnPlay= (Button)findViewById(R.id.play);
btnPause= (Button)findViewById(R.id.pause);
btnStop= (Button)findViewById(R.id.stop);
btnFinish= (Button)findViewById(R.id.finish);
btnExit= (Button)findViewById(R.id.exit);
btnPlay.setOnClickListener(this);
btnPause.setOnClickListener(this);
btnStop.setOnClickListener(this);
btnFinish.setOnClickListener(this);
btnExit.setOnClickListener(this);
connection();
}
private void connection() {
Log.v(TAG , TAG+"connection");
Intent intent = new Intent("xuxm.demo.service02.MusicService");
//this.startService(intent);
this.bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId())
{
case R.id.play:
MyMediaController.play.execute();
m_TextView_text.setText(R.string.play);
Log.d(TAG, "play.......");
break;
case R.id.pause:
MyMediaController.pause.execute();
m_TextView_text.setText(R.string.pause);
Log.d(TAG, "pause.......");
break;
case R.id.stop:
MyMediaController.stop.execute();
m_TextView_text.setText(R.string.stop);
Log.d(TAG, "stop.......");
break;
case R.id.finish:
Log.d(TAG, "close.......");
this.finish();
break;
case R.id.exit:
Log.d(TAG, "exit.......");
this.finish();
this.stopService(new Intent("xuxm.demo.service02.MusicService"));
break;
default:
}
}
//调用bindService后 Service调用onBind()后 回调此函数
private ServiceConnection myServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service.Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
Log.v(TAG , TAG+ "onServiceConnected");
// TODO Auto-generated method stub
bindMusicService = ((LocalBinder)binder).getService();
//给Controller设置Service初始化的MediaPlayer
MyMediaController.mediaPlayer=bindMusicService.getMyMediaPlayer();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
// TODO Auto-generated method stub
bindMusicService=null;
Log.v(TAG, "..............onServiceDisconnected");
}
};
//当Activity finish时必须解绑 不然会出现溢出
@Override
public void finish() {
// TODO Auto-generated method stub
super.finish();
this.unbindService(myServiceConnection);
}
}
运行时,发现调用次序是这样的:
bindService:
1.MusicService02: onCreate
2.MusicService02: onBind
3.Activity: onServiceConnected
unbindService: 只是调用onDestroy
可见,onStart是不会被调用的,而onServiceDisconnected没有调用的原因在上面代码的注释有说明。
此时,activity停止时,service也停止了。
2.2 bind service的一些问题
前面的分析中可以看到Activity和Service,context.startService对应着Service中的onStart()方 法,context.onBindService对应的是Service中的onBind()方法。当我们继想绑定一个Service又想在 Activity停止时,Service不会停止,我们可以先StartService,然后再BindService()。这时候的流程图如下所示:
此时需要注意一个问题,当Activity退出的时候,Sercvice并不会停止,此时我们可以再进入Activity重新绑定,当这时候 Service就会调用onRebind()方法,但是调用onRebind()方法的前提是先前的onUnbind()方法执行成功,但是使用 super.onUnbind(intent)是执行不成功的,这时候我们要手动的使其返回true,再次绑定时Rebind()就会执行。否则,如果退 出时不显示的指定onUnbind()为成功的话(为false),那么重新启动此Activity来绑定服务时,Service的onBind()方法 和onReBind都不会执行,但是ServiceConnection方法确一定会回调了。这说明在Service中的onBind()方法不同于 onStart()方法不能被重复调用。