主要用于执行某些需要在后台长期运行的任务,并且是不受界面影响的,必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态
默认是在主进程的主线程中执行,因此若耗时操作,仍需要开启子线程执行
但是进程是有优先级的 给这个Service所在的进程(默认即为主进程) 设置为服务进程或者前台进程,这样能更好实现主进程的保活
若要自建一个进程中执行,则可以通过android:process属性进行设置 (Activity 等其他组件也是一样)
若名称以:(冒号)开头 则是该应用私有的 别的进程不能访问
若是符合包名的小写字母 则是相当于开启了一个新的进程或者可以说是应用
是会执行application的onCreate操作的
常用系统服务
后台service由系统启动时被SystemService服务启动
通过当前活动.getSystemService(name)并存于相应类的变量中 这些变量再调用一些方法
比如 WIFI连接查看 系统音量 短信 电量等等
实现:
主要有两种启动模式:
第一种:
生命周期见图一左侧
在Activity中:
Intent intent = new Intent(MainActivity.this,XXXService.class);
startService(intent);// (服务与开启者无联系的启动形式 )
创建Service服务类继承Service,实现下面的方法1,2,3,5
(若不是通过 new service 创建的 则要自己去 manifest 的application标签添加 service标签)
第二种:服务会与组件binding(绑定)
生命周期见图一右侧
创建服务类继承 Service 实现下面的方法1,3,4,5 注意是1,3,4,5!!!
(若不是通过 new service 创建的 则要自己去 manifest 的application标签添加 service标签)
方法1 : onCreate:
如果多次执行了Context的startService或者bindService方法启动Service,Service方法的onCreate方法只会在第一次创建Service的时候调用一次,以后均不会再次调用我们可以在onCreate方法中完成一些Service初始化相关的操作
方法2 :onStartCommand(Intent intent,int flag, int startId)
如果多次执行了Context的startService方法,那么Service的onStartCommand方法也会相应的多次调用
android在应用crash了之后,会默认重启service
可以通过onStartCommand的设置返回参数进行控制
START_NOT_STICKY
表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service,如果想重新实例化该Service,就必须重新调用startService来启动
START_STICKY
表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,这时onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息
START_STICKY_COMPATIBILITY
这个其实是用来兼容api5 一下的,这个的作用和START_STICK一样,但是这个返回值不能保证系统一定会重新创建service
START_REDELIVER_INTENT
Android系统会将再次重新创建该Service,并执行onStartCommand回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。
itent
intent就是startService(Intent intent)中的intent
flags
表示启动服务的方式:START_FLAG_REDELIVERY, 或者START_FLAG_RETRY
START_FLAG_REDELIVERY:你实现onStartCommand()来安排异步工作或者在另一个线程中工作,需要使用START_FLAG_REDELIVERY来 让系统重新发送一个intent。这样如果你的服务在处理它的时候被Kill掉, Intent不会丢失.
START_FLAG_RETRY:表示服务之前被设为START_STICKY,则会被传入这个标记。
startId
onStartCommand的启动次数,第一次通过startService启动为是1,不断startService启动依次累加,一般配合stopSelf(startId)使用可以看IntentService中使用(不确定,待测试)
方法3 :onBind
必须重写的方法
通过startService使用Service时,我们在重写onBind方法时,只需要将其返回值设为null即可。bindService时,用于返回binder对象
方法4 : onUnbind
解除绑定时调用unbindService(connect)会调用该方法,该方法调用结束后会解除绑定,并且调用onDestroy()方法
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
//松绑Service,会触发onDestroy()
if(mediaPlayer.isPlaying()){
mediaPlayer.stop();
}
return super.onUnbind(intent);
}
方法5 onDestroy
在service销毁做相关释放操作,
应该在onDestroy()
而不是 stopService()
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
//先停止 再释放
if(mediaPlayer.isPlaying()){
mediaPlayer.stop();
}
mediaPlayer.release();
}
至此,第一种方式完成,第二种方式还需要下面的步骤:
再在该服务类中声明一个类继承Binder{写各种方法的功能实现 oncreate() onBind()之类的}
class MyMusicBinder extends Binder{
//返回Service对象
MusicService getService(){
return MusicService.this;
}
}
在Activity中创建一个connector类 (连接服务的Client)监听Service状态的变化
private class MusicConnector implements ServiceConnection {
//成功绑定时调用 即bindService()执行成功同时返回非空Ibinder对象
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//写成功绑定时要执行的内容
}
//不成功绑定时调用
@Override
public void onServiceDisconnected(ComponentName componentName) {
//写不成功绑定时要执行的内容
Log.i("binding is fail", "binding is fail");
}
}
在相应事件(点击事件或者onCreate)等:
Intent intent = new Intent();
intent.setClass(MainActivity.this, MusicService.class);
MusicConnector connect_1= new MusicConnector();
在相应事件(点击事件等)中,进行服务与组件的绑定
bindService(intent,connect_1,flag);//(服务会与组件绑定)
service拿对象 得bindService才能拿
要在Service启动后 根据其他情况才做一些操作,得通过广播发信息过去,或者EventBus
flag 指定为0 绑定时不自动创建,指定为 BIND_AUTO_CREATE 绑定时自动创建
在相应事件(点击事件等)中,进行服务与组件的解除绑定
unbindService(connect_1);//解除绑定
会调用 onUnbind()
当组件销毁(关闭) 也会自动解除绑定 并 销毁 onDestroy()
至此,第二种绑定方式也完成!
服务停止:
stopService(intent);
or
stopSelf();
会调用onDestroy()
注意点:
1 记得在在各个位置调用stopService 各个界面 各个该结束的地方
2 crash了 默认是会重启服务的 要根据自己的需求设置 START_STICK参数
Service进程启动过程(startService())原理分析:https://blog.csdn.net/luoshengyang/article/details/6677029
IntentService
Service的子类 主要特点是: service内部 默认提供了一个线程,并且只能处理一个线程,并且当任务结束会自动关闭service
使用:
1 自定义MyIntentService 继承 IntentService
2 重写方法:
@Override
protected void onHandleIntent(@Nullable Intent intent) {
在方法内部做操作即可
但是如果要绑定服务的话,还是要重写onBind 返回一个IBinder
JobIntentService
1 不具备实时性质 不适合实时更新UI
2 无法保证它会立即执行作业*
3 没有主动关闭的接口,只适用于短耗时的后台任务,如果需要在后台执行长期的任务或者循环任务,还是得开启前台服务
基本使用
1 要有WAKE_LOCK permission
2
<service
android:name=".service.XXJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
1 提供enqueueWork静态方法给外部调用
companion object {
fun enqueueWork(context: Context, work: Intent) {
JobIntentService.enqueueWork(context, SSDPDiscoveryJobService::class.java, JOB_ID, work)
}
}
2 重写onHandleWork 进行任务的处理
是会使用AsyncTask来处理work的,所以不需要考虑主线程阻塞的问题
override fun onHandleWork(intent: Intent) {
// We have received work to do. The system or framework is already
// holding a wake lock for us at this point, so we can just go.
val aa = intent.getStringExtra("lable")
3 重写onDestroy方法
当onHandleWork执行完 则会自动调用onDestroy 没有主动stop的接口 只能等onHandleWork执行完 因此不适合循环 长期任务
override fun onDestroy() {
问题:启动不了JobIntentService
**原因:**之前的服务开启了 没有关闭(没有调用onDestroy)
问题 Stopping service due to app idle
Background start not allowed: service Intent
java.lang.IllegalStateException: Not allowed to start service Intent
原因:
Android8.0以上 电池优化策略 应用在后台经过 idle timeout后 会禁用后台服务 stopService 并禁止启动服务
解决:
1 设置为前台服务
2 用JobIntentService
3 用JobService+JobScheduler 参考:https://blog.csdn.net/weixin_37577039/article/details/80041624
利用service进行背景音乐播放的事例参考博客:
http://blog.csdn.net/weixin_37577039/article/details/78775931