1. 定义一个服务
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestory() {
super.onDestory();
}
}
(1)onCreate:服务首次创建时执行一次;
(2)onStartCommand:服务启动时就执行(每次调用startService时都会执行);
(3)onDestory:服务销毁时执行一次;
2. AndroidManifest.xml文件中注册服务
<service
android:name=".MyService"
android:enabled="true"
android:exported="false">
</service>
(1)android:name,指定的是注册的服务类所在的路径(相对路径或完整包路径);
(2)android:enabled,是否启用当前服务;
(3)android:exported,当前服务是否允许其他进程使用;
上面两个步骤基本上就是完整的定义一个服务的流程,下面介绍如何使用服务:
3. 启动和停止服务
启动和停止服务也是通过Intent来完成的。例如:
启动服务
Intent startServiceIntent = new Intent(MainActivity.this, MyService.class);
startService(startServiceIntent);
停止服务
Intent stopServiceIntent = new Intent(MainActivity.this, MyService.class);
stopService(stopServiceIntent);
通过startService启动服务后,服务就与启动服务的组件没有关系了,会一直处于运行状态(除非内存不足,系统自动回收),所以我们需要在必要时主动停止服务。还有一种停止服务的方式是在定义服务的MyService类中调用:stopSelf()。
4. 活动(Activity)与服务(Service)间的通信
上面的例子中确实能够创建并在activity中启动/销毁一个服务,但是activity却无法与MyService类进行通信。这时就需要通过bindService的方式绑定并创建服务。同时在MyService类中重写onBind方法,借助IBinder实例作为通信媒介。
(1)在MyService类中创建一个子类继承Binder类,定义暴露出去供activity中代码调用的方法:
class DownloadBinder extends Binder {
public void startDownload() {
Log.d("MyService", "startDownload");
}
public int getProgress() {
Log.d("MyService", "getProgress");
return 0;
}
}
(2)MyService类中实例化自定义的DownloadBinder类,并在onBind中返回该实例:
private DownloadBinder mBinder = new DownloadBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
(3)在服务的主动绑定方,例如MainActivity中通过匿名类的方式实现ServiceConnection接口:
private MyService.DownloadBinder mDownloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mDownloadBinder = (MyService.DownloadBinder)service;
mDownloadBinder.startDownload();
mDownloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
(1)onServiceConnected:服务连接时调用;
(2)onServiceDisconnected:服务断开时调用;
在onCreate方法中,绑定服务:
protected void onCreate(Bundle savedInstanceState) {
Intent bindServiceIntent = new Intent(this, MyService.class);
bindService(bindServiceIntent, connection, BIND_AUTO_CREATE);
}
注:BIND_AUTO_CREATE是一个标志位,表示在绑定时,就自动创建服务,也就是说MyService类中的onCreate方法会执行。
如果想要解绑可以使用:unbindService(connection);
一个服务可以与多个Activity进行绑定,而这些Activity在绑定成功后获取到的IBinder实例都会是同一个。而且如果既调用了startService来开启服务,又调用了bindService来绑定服务(这是允许的),那么在销毁该服务时,必须同时调用stopService和unbindService。
备注1:使用bindService执行服务绑定时,对应的执行方法如下:MyService#onCreate -> MyService#onBind -> ServiceConnection#onServiceConnected。并且每次调用bindService,这三个方法都只会在首次执行一次。
备注2:调用unbindService时,会执行onDestroy方法。
备注3:调用unbindService时,一定要确保服务处于绑定状态,否则会抛出异常(Service not registered),换言之就是不支持多次重复调用unbindService方法。
备注4:通过bindService启动服务时,被启动的服务和Activity会关联起来,所以在退出Activity时,一定要调用unbindService方法进行解绑,或者会抛出异常,提示ServiceConnection泄露的错误。
备注5:onServiceDisconnected并不会在unbindService后执行,onServiceDisconnected只会在服务崩溃或被杀掉后执行,详见官网解释。
5. 前台服务
后台服务是有可能在内存不足时被系统回收的,此时我们可以创建一个前台服务。同时前台服务还可以关联一个长时间存在的通知(例如某些天气和音乐应用)。
创建前台服务可以通过startForegroundService来完成,但是这是api26中才提供的方法,所以试用前需要判断一下SDK版本,例如:
Intent startProxyServiceIntent = new Intent(this, ProxyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(startProxyServiceIntent);
} else {
startService(startProxyServiceIntent);
}
将前台服务关联到某个通知:
startForeground(notifyId, notification);
停止前台服务,可以使用stopForeground:
stopForeground(true); // true表示停止前台服务的同时移除通知栏关联的通知
stopSelf();
6. 使用服务的注意事项
(1)服务运行于管理它的进程的主线程上,服务并不会创建自己的线程。如果不想自行在服务创建线程,可以直接使用系统提供的IntentService类,其中的onHandlerIntent方法中的代码默认在工作线程执行。
(2)创建服务存在三种状态:启动状态、绑定状态、二者兼有。
启动状态:通过startService启动服务,仅需要实现onCreate、onStartCommand和onDestory方法,这种方式启动的服务与启动服务的activity生命周期无关,会一直常驻在后台,除非低内存状况下自动回收。
绑定状态:通过bindService绑定的服务,这种方式需要实现onBind方法,并且启动的服务于该服务所绑定的activity生命周期有关,当所有绑定某个服务的activity都解绑后,服务会自行销毁。
二者兼有:不仅通过startService启动服务,同时也在某个activity上通过bindService绑定了该服务。这样如果想要完全销毁该服务,必须调用stopService方法,同时所有相关的activity都主动解绑才行。
(3)安卓系统仅当内存不足且必须回收系统资源来显示用户关注的activity时,才会强制停止服务。如果服务绑定到用户一直关注的activity,则会减小被停止的概率。如果服务被声明为前台运行(前台服务),则基本不会停止。