Android四大组件之Service
一、概述
Service
是Android
四大组件之一,是一个计算型组件。它在Android
开发中扮演着一个重要的角色,虽然他不如Activity
那么常见,但是在很多App
中都有着实际的应用场景。
Service
的运行不依赖于任何用户界面,所以它非常适合用于去执行那些不需要和用户交互但却需要长期运行的任务,如App
的更新,音乐播放器等。
二、生命周期
官方文档提供的Service
生命周期图如下:
由图中可知
Service
有两种启动方法,其中共涉及了5个回调方法:
-
onCreate()
:服务第一次被创建时调用 -
onStartComand()
:服务启动时调用 -
onBind()
:服务被绑定时调用 -
onUnBind()
:服务被解绑时调用 -
onDestory()
:服务停止时调用
三、启动方式
上面已经说过了,Service
的启动方式有两种:
- 通过
Context.startService()
方法启动一个Service
,并回调服务中的onStartCommand()
方法。如果该服务之前还没被创建过,那么回调的顺序为:onCreate()
->onStartCommand()
。服务启动后会一直保持运行状态,直到stopService()
或stopSelf()
方法被调用,服务停止并回调onDestory()
。另外,无论调用多少次startService()
方法,只需调用一次stopService()
或stopSelf()
方法,服务就停止了。 - 通过
Context.bindService()
绑定一个Service
,并回调服务中的onBind()
方法,同样的如果是第一次创建则会先调用onCreate()
方法。之后,调用方法可以获取到onBind()
方法里返回IBinder
对象的实例,从而实现和服务进行通信。只要调用方与服务之间的连接没有断开,服务就一直保持运行状态,直到调用unbindService()
方法服务才会停止,回调顺序onUnBind()
->onDestory()
。
尽管Service
组件是用于执行后台计算的,但是它本身是运行在主线程中的,所以耗时的后台计算仍然需要在单独的线程中完成。但是处于这种状态时,外界可以很方便的和Service
组件进行通信。
区别:startService()
开启服务后便于Activity
没有关联了,即不受影响,独立运行;而bindService()
开启服务后与Activity
仍存在关联,退出Activity时必须调用unBindService
方法,否则会报ServiceConnection
泄漏的错误。
注意:这两种启动方式并不冲突,当使用Context.startService()
启动服务后仍可以再用bindService()
方法绑定服务,但是需要同时调用stopService()
和onUnBind()
方法才能停止服务。
四、基本用法
Service
其实有三种类型:普通Service、前台Service和系统Service。
前台Service就是利用Notification
然后调用startForeground
方法以通知的形式将Service显示出来。
系统Service,顾名思义就是系统的一些服务了,常见的系统服务有:
通过
getSystemService
方法传入name
值即可得到相应的服务对象。
而普通Service
对于我们而言应用场景就比较广泛了,下面将使用一个音乐播放器的小Demo来展示如何注册并使用一个普通广播:
-
首先我们需要实现
MusicService
并继承Service
这个类,以提供播放、暂停和停止音乐的方法:
public class MusicService extends Service {
private final String TAG = "MusicService";
private MediaPlayer mediaPlayer;
private int startId;
//枚举类型 标记播放状态
public enum Control {
PLAY, PAUSE, STOP
}
public MusicService() {
}
@Override
public void onCreate() {
if (mediaPlayer == null) {
//创建MediaPlayer对象 R.raw.my_music为mp3格式资源文件夹中的音乐
mediaPlayer = MediaPlayer.create(this, R.raw.my_music);
mediaPlayer.setLooping(false); //是否单曲循环
}
Log.d(TAG, "onCreate: ");
super.onCreate();
}
//使用startService方法启动Service的一个生命周期方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.startId = startId;
Log.d(TAG, "onStartCommand: ---------startId=" + startId);
Bundle bundle = intent.getExtras();
if (bundle != null) {
Control control = (Control) bundle.getSerializable("Key");
if (control != null) {
switch (control) {
case PLAY:
play();
break;
case PAUSE:
pause();
break;
case STOP:
stop();
break;
}
}
}
return super.onStartCommand(intent, flags, startId);
}
//继承Service必须实现的一个方法
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return null;
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy: ");
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release(); //释放资源
}
super.onDestroy();
}
private void play() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
private void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
private void stop() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
stopSelf(startId); //停止服务
}
}
-
在
AndroidManifest.xml
中注册Service
:
<!--添加label标签以便在系统中识别-->
<service
android:name=".MusicService"
android:label="@string/app_name" />
- 在布局文件中添加三个按钮分别控制音乐播放器的状态,这里就不展示
xml
文件中的布局代码了,直接看Activity里的代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button playButton = findViewById(R.id.btn_play);
playButton.setOnClickListener(this);
Button pauseButton = findViewById(R.id.btn_pause);
pauseButton.setOnClickListener(this);
Button stopButton = findViewById(R.id.btn_stop);
stopButton.setOnClickListener(this);
}
//播放音乐
public void playMusic(View view) {
Intent intent = new Intent(this, MusicService.class);
Bundle bundle = new Bundle();
bundle.putSerializable("Key", MusicService.Control.PLAY);
intent.putExtras(bundle);
startService(intent);
}
//暂停音乐
public void pauseMusic(View view) {
Intent intent = new Intent(this, MusicService.class);
Bundle bundle = new Bundle();
bundle.putSerializable("Key", MusicService.Control.PAUSE);
intent.putExtras(bundle);
startService(intent);
}
//停止音乐
public void stopMusic(View view) {
Intent intent = new Intent(this, MusicService.class);
Bundle bundle = new Bundle();
bundle.putSerializable("Key", MusicService.Control.STOP);
intent.putExtras(bundle);
startService(intent);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_play:
playMusic(v);
break;
case R.id.btn_pause:
pauseMusic(v);
break;
case R.id.btn_stop:
stopMusic(v);
break;
}
}
}
Demo效果截屏:
log输出:
根据测试可以得出结论:只有第一次启动服务时才会执行onCreate()
方法,之后再次启动则只会调用onStartCommand()
方法,且每次启动Service都会有一个startId
用于唯一标识每次对Service发起的处理请求。调用stopSelf()
方法后会回调onDestroy()
方法,之后服务被终止。
五、IntentService
IntentService
是继承于Service
并处理异步请求的一个类,在IntentService
内有一个工作线程来处理耗时操作,启动IntentService
的方式和启动传统的Service
一样,同时,当执行完后,IntentService
会自动停止,而不需要我们手动去控制或stopSelf()
。
由于大多数启动服务都不必同时处理多个请求(像上一个Demo中Service就处理了多个请求),因此使用IntentService
类实现服务也许是最好的选择。IntentService
内部采用了HandlerThread
,所以某种程度上也可以把它当成是一个特殊的线程,有关它的更多信息可以到我的另一篇文章中Android的线程和线程池了解。