如何写一个正经的Android音乐播放器 二
与音乐播放Service交互
稍有经验的同学都知道,将长时间的操作放在Service中进行,如何做到界面和音乐播放Service的有效沟通呢,在这一章中给出我的答案,同样希望大神们给出指点。
希望你阅读(自行翻墙):
Service的API Guide:http://developer.android.com/guide/components/services.html
Service的API Guide中的有关bound service中的部分:
http://developer.android.com/guide/components/bound-services.html
Service的API:http://developer.android.com/reference/android/app/Service.html
首先实现一个Service类,我们命名之为MainService,那么我们应该如何启动这个Service呢?启动Service一个Service有两种方法:一种是startService (Intent);另外一种是:bindService (Intent);
第一种启动方式,适用于Service独立完成任务,例如说一个下载,如果不需要暂停或者取消的话,可以这样来做。但是,我们这是音乐播放应用,把MediaPlayer放在Service中执行播放,这样的方式很难有暂停等交互。(其实也可以这样做,例如每次交互操作都用startService来做,通过Intent把暂停等命令出入进去,进度条也可以通过广播来发送出去,但是这样做,感觉很丑陋,肯定不是常规的高效做法);
第二种启动方式,可以在onServiceConnected中,获得一个Service对象(可能不是一个Service对象,至少是一个可以接触Service内部操作的句柄)。这样,便可以轻松操作播放和接收进度通知。但是这种方式有弊端,一旦与之绑定的context退出,则绑定接触,Service也会被回收(但是不会执行onDestory方法);
那么是不是就只是简单用第二种启动方式?
当然不是这样,在谷歌官方文档中,有交代,这两种方式并不冲突,而且十分适用于音乐播放类的应用。
原文如下:
A bound service
The service is created when another component (a client) calls bindService(). The client then communicates with the service through an IBinder interface. The client can close the connection by calling unbindService(). Multiple clients can bind to the same service and when all of them unbind, the system destroys the service. (The service does not need to stop itself.)
These two paths are not entirely separate. That is, you can bind to a service that was already started withstartService(). For example, a background music service could be started by calling startService() with anIntent that identifies the music to play. Later, possibly when the user wants to exercise some control over the player or get information about the current song, an activity can bind to the service by calling bindService(). In cases like this, stopService() or stopSelf() does not actually stop the service until all clients unbind.
简单理解是:可以有多个client去绑定同一个Service,并且只有所有绑定的client都解除绑定以后,Service就会被destory掉,不需要执行stopSelf方法,同样不会回调到onDestory方法;一个Client可以绑定一个已经start的Service,这样绑定的话,要停止这个service,则必须先解除一切绑定的client,然后再调用stopService或者stopSelf方法。
得到官方的说法后,就可以放心大胆的写代码了。
为了方便,我们定义一个自己的Application类——BeApplication,来执行相关操作。
public class BeApplication extends Application implements ServiceConnection {
@Override
public void onCreate() {
super.onCreate();
startMainService();
bindMainService();
}
public void startMainService () {
Intent it = new Intent (this, MainService.class);
startService(it);
}
public void stopMainService () {
Intent it = new Intent(this, MainService.class);
stopService(it);
}
private void bindMainService () {
Intent it = new Intent (this, MainService.class);
this.bindService(it, this, Service.BIND_AUTO_CREATE);
}
private void unbindMainService () {
this.unbindService(this);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (service instanceof MainService.ServiceBinder) {
MainService.ServiceBinder binder = (MainService.ServiceBinder)service;
mMainService = binder.getService();
mMainService.registerServiceCallback(mPlayManager);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Toast.makeText(this, "onServiceDisconnected name=" + name, Toast.LENGTH_LONG).show();
}
}
在这个Application类的onCreate方法中,我们先启动一个Service,然后绑定这个Service。注意,让自己的BeApplication能够运行,要在manifest文件中的<application/>标签下,定义android:name=”包名加我们application类的类名”。
现在写我们用音乐播放的Service类——MainService,同样记得在manfest文件中声明这个Service。
public class MainService extends Service {
public static class ServiceBinder extends Binder {
private MainService mService = null;
public ServiceBinder(MainService service) {
mService = service;
}
public MainService getService () {
return mService;
}
}
@Override
public IBinder onBind(Intent intent) {
return new ServiceBinder(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
//return super.onStartCommand(intent, flags, startId);
}
}
注意之前在BeApplication中的onServiceConnected方法,我们在那个方法中,取得了由MainService中的onBind方法返回的IBinder对象,并通过这个对象,我们在BeApplication中拿到这个用于播放音乐的Service,这样以来,与Service交互容易的多。
也许你会问,这样已经拿到service对象了,那有何必要去start这个service呢?
注意MainService中的onStartCommend方法,此方法返回了一个int常量,对于这个常量,官方有这样的解释:
START_STICKY
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.
另外 还有两个常用的常量:START_NOT_STICKY 和START_REDELIVER_INTENT ,可以在此链接进行扩展阅读:http://developer.android.com/guide/components/services.html#ExtendingService
此变量的功能在于,一旦系统杀死了这个service,在重新启动这个service的时候,onStartCommand中的intent不起作用。这适用于,在onStartCommand中,并不执行工作的Service。
附service生命周期流程图:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。