Service的生命周期
Service是Android四大组件之一,它通常用来实现需要在后台进行耗时操作的需求,它的生命周期如下:
可以看到Service的生命周期分成了两条线,左边这条是使用startService()
方法启动Service后走的生命周期,右边这条是使用onBind()
方法绑定Service后走的生命周期,下面我们先来学习使用startService()
的情况下Service的生命周期。
创建一个Service类,命名为MyService,令其继承自Service类,并重写其下的生命周期回调函数:
- onCreate
- onStartCommand
- onBind
- onUnBind
- onDestory
如果是在AndroidStudio中直接创建的Service类,那么AndroidStudio会自动为我们继承自Service类并且在Manifest配置文件中注册该Service,否则我们需要亲自去Manifest文件中注册刚刚创建的MyService。
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"/>
public class MyService extends Service {
public MyService() {
}
//创建Service
@Override
public void onCreate() {
super.onCreate();
Log.d("TAG", "Service:onCreate()");
}
//启动Service
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("TAG", "Service:onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
//绑定Service
@Override
public IBinder onBind(Intent intent) {
Log.d("TAG", "Service:onBind()");
return null;
}
//解绑Service
@Override
public boolean onUnbind(Intent intent) {
Log.d("TAG", "Service:onUnBind()");
return super.onUnbind(intent);
}
//销毁Service
@Override
public void onDestroy() {
Log.d("TAG", "Service:onDestroy()");
super.onDestroy();
}
}
接着我们在Activity中放置四个按钮,其功能分别为启动服务、停止服务、绑定服务和解绑服务,id分别为start、stop、bind、unbind,并且每个按钮都指定了一个点击事件处理函数operate()
:
//四个按钮的点击事件处理函数
public void operate (View view) {
switch (view.getId()) {
case R.id.start: {
//启动服务
Intent intent = new Intent(this, MyService.class);
startService(intent);
break;
}
case R.id.stop: {
//停止已经启动的服务
Intent intent = new Intent(this, MyService.class);
stopService(intent);
break;
}
case R.id.bind: {
//绑定服务
break;
}
case R.id.unbind: {
//解绑服务
break;
}
}
}
分析上面的代码可以知道,我们通过调用startService()
和stopService()
来启动和停止一个服务,参数都是Intent对象。
接下来我们首先点击第一个按钮启动服务,查看控制台输出信息:
D/TAG: Service:onCreate()
D/TAG: Service:onStartCommand()
可以看到onCreate()
和onStartCommand()
方法被执行了,这说明我们在启动Service的时候会回调Service下的onCreate()
和onStartCommand()
方法。
这时我们再点击停止服务的按钮,查看控制台输出信息:
D/TAG: Service:onDestroy()
这次回调了Service的onDestroy()
方法销毁了Service。
了解了startService()
启动Service后的生命周期流程后,我们再接着学习使用bindService()
绑定Service时走过的生命周期。
首先完成点击绑定和解绑按钮时的事件处理函数,bindService()
绑定服务函数除了需要传入Intent意图对象外,还需要传入一个ServiceConnection类和一个flags(BIND_AUTO_CREATE 表示在绑定Service的时候也创建Service),由于ServiceConnection对象不仅需要用在bindService()
中,也需要用在解绑服务的方法unbindService()
中,因此我们将ServiceConnection声明为全局变量并实例化:
//绑定和解绑Service时需要用到的ServiceConnection类
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
......
......
//四个按钮的点击事件处理函数
public void operate (View view) {
switch (view.getId()) {
......
......
case R.id.bind: {
//绑定服务
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
break;
}
case R.id.unbind: {
//解绑服务
unbindService(serviceConnection);
break;
}
}
}
在停止服务后,我们点击绑定服务按钮,查看控制台输出语句:
D/TAG: Service:onCreate()
D/TAG: Service:onBind()
可以看到,我们如果在不启动服务的情况下直接绑定服务,系统会依次回调onCreate()
和onBind()
方法来创建和绑定服务,但注意此时后台实际上是没有服务的,因为服务在没有回调onStartCommand()
方法前并不会活跃在后台进程中!即使这个服务被创建了(不相信的同学可以在Android后台服务查看一下)
然后我们点击解绑服务,查看控制台输出语句:
D/TAG: Service:onUnBind()
D/TAG: Service:onDestroy()
在我们解绑一个没有被启动(注意这几个字)的Service时,会依次回调Service的onUnbind()
和onDestroy()
来解绑和销毁Service。
接下来我们再进行几个尝试,深入了解一下Service的生命周期。
(1)、点击启动服务,再点击绑定服务,查看控制台输出语句:
D/TAG: Service:onCreate()
D/TAG: Service:onStartCommand()
D/TAG: Service:onBind()
(2)、在(1)的基础上,点击解绑服务,查看控制台输出语句:
D/TAG: Service:onUnBind()
(3)、在(1)的基础上,点击停止服务,查看控制台输出语句:
D/TAG: Service:onCreate()
D/TAG: Service:onStartCommand()
D/TAG: Service:onBind()
(4)、在(3)的基础上,点击解绑服务,查看控制台输出语句:
D/TAG: Service:onUnBind()
D/TAG: Service:onDestroy()
总结:只有回调了onStartCommand()
方法的Service才是一个启动的Service,没有被启动的Service是无法发挥功能的,同时也不会出现在Android系统后台,因此直接绑定服务是无法让服务生效的;直接绑定的服务在解绑时会依次调用onUnbind
和onDestroy
,已经启动的绑定了的服务在解绑时只会执行onUnbind
而不会再执行onDestroy
来销毁Service;绑定服务会随着Activity的结束而结束,但是启动服务不受Activity的影响。
Service与Activity的通信
Service在大多数情况下都是用来进行后台耗时任务的,但如果只是单纯的startService
后Activity是无法知道Service在后台执行耗时任务的进度的,因此Activity需要与Service进行通信,这就需要用到我们之前没有详细说明的IBinder接口和ServiceConnection接口。
首先在MyService的onCreate
方法中开启一个新线程进行耗时操作:
//线程执行进度
private int process;
@Override
public void onCreate() {
super.onCreate();
Log.d("TAG", "Service:onCreate()");
//开启一个新线程从1数到100,循环一次休眠一秒,用来模拟耗时任务
new Thread(){
@Override
public void run() {
super.run();
for (process = 1; process <= 100; process++) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
至于为什么不在onStartCommand
中开启新线程,是因为如果在已经创建并启动了这个服务的基础上再次启动服务,系统会再次回调onStartCommand
方法而不会回调onCreate
方法,这样可以防止同时出现两个耗时的线程。
在之前的onBind()
中我们可以看到,这个方法需要返回一个实现了IBinder接口的类,由于之前我们不需要Activity和Service之间进行通信,因此我们返回了一个null:
//绑定Service
@Override
public IBinder onBind(Intent intent) {
...
return null;
}
于是我们这次尝试着返回一个实现了IBinder接口的类试试看:
@Override
public IBinder onBind(Intent intent) {
Log.d("TAG", "Service:onBind()");
return new IBinder() {
@Override
public String getInterfaceDescriptor() throws RemoteException {
return null;
}
@Override
public boolean pingBinder() {
return false;
}
@Override
public boolean isBinderAlive() {
return false;
}
@Override
public IInterface queryLocalInterface(String descriptor) {
return null;
}
@Override
public void dump(FileDescriptor fd, String[] args) throws RemoteException {
}
@Override
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
}
@Override
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return false;
}
@Override
public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
}
@Override
public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
return false;
}
};
}
可以看到如果我们要自己去实现IBinder接口的话,我们需要重写很多没有见过的方法,为了节约开发者的时间和简化开发难度,Android提供了一个Binder类,Binder是一个实现了IBinder接口并且实现了其中抽象方法的类,因此我们可以创建一个继承自Binder的内部类来免去实现IBinder接口的步骤以及在我们自己的类中编写一些我们自己需要的方法,例如下面的MyBinder类:
class MyBinder extends Binder {
//获取线程完成进度的自定义方法
public int getProcess () {
return process;
}
}
在MyBinder类中我们提供了一个对外的接口getProcess
来让Activity能访问到Service中耗时任务的进度,但是现在的问题是,接口是有了,我们如何让Activity能调用这个接口呢?
还是要看回onBind
方法,这个方法是用来绑定Service和Activity的,因此要想实现Activity和Service的通信,这个方法返回的IBinder尤为重要,由于我们刚刚创建了一个MyBinder内部类,那么我们就在这个方法中返回一个MyBinder的实例化对象:
//绑定Service
@Override
public IBinder onBind(Intent intent) {
Log.d("TAG", "Service:onBind()");
return new MyBinder();
}
现在让我们回想一下,之前我们在Activity中调用绑定Service的方法bindService
的时候,除了传入一个Intent和一个flags外,还传入了一个ServiceConnection类的对象,在实例化这个对象的时候我们的代码如下:
//绑定和解绑Service时需要用到的ServiceConnection类:用户绑定客户端和Service
private ServiceConnection serviceConnection = new ServiceConnection() {
//当客户端和Service正常连接时调用
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
Log.d("ServiceConnection", "onServiceConnected()");
}
//当客户端和Service丢失连接时调用
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("ServiceConnection", "onServiceDisconnected()");
}
};
其中的onServiceConnected
方法会在Service和Activity发起通信(绑定[其实并不准确])时调用,我们观察一下可以发现,这个方法里面会传过来一个IBinder类型的参数,再想想我们之前的onBind
方法里,是不是也正好返回了一个IBinder类型的参数?没错,onBind
方法里返回的IBinder就正好传入了onServiceConnected
!这样,Service和Activity的通信桥梁就通过ServiceConnection和IBinder搭建起来了,我们就可以把在onServiceConnected
方法中获取到的IBinder强制类型转换为MyBinder,从而调用我们之前在MyBinder中声明的接口来知道Service中耗时任务的进度了!
但是这里我们还要了解一个知识点:在创建并开启一个服务后,我们将其绑定到Activity上后解绑,之后再次绑定的时候onBind
方法是不会再次执行的,也就是说,onBind
方法只会在Service第一次绑定的时候被调用!但是每次我们再次绑定的时候,不论onBind
执行与否,ServiceConnection的onServiceConnected
是一定会被执行的,因此我们就可以通过解绑再绑定的方式获取到后台Service耗时任务的完成进度了!
//绑定和解绑Service时需要用到的ServiceConnection类:用户绑定客户端和Service
private ServiceConnection serviceConnection = new ServiceConnection() {
//当客户端和Service正常连接时调用
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
Log.d("ServiceConnection", "onServiceConnected()");
MyService.MyBinder myBinder = (MyService.MyBinder) iBinder;
int process = myBinder.getProcess();
Log.d("ServiceConnection", "当前进度:" + process);
}
//当客户端和Service丢失连接时调用
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("ServiceConnection", "onServiceDisconnected()");
}
};
我们执行一次绑定->解绑->绑定的效果如下:
D/TAG: Service:onCreate()
D/TAG: Service:onStartCommand()
D/TAG: Service:onBind()
D/ServiceConnection: onServiceConnected()
D/ServiceConnection: 当前进度:4
D/TAG: Service:onUnBind()
D/ServiceConnection: onServiceConnected()
D/ServiceConnection: 当前进度:13
这样我们就了解了Service、IBinder和ServiceConnection各自的作用了。