Android 8.0之后如何正确使用Service
这篇文章简单介绍Service的一些使用注意事项,可以作为参考。
何时使用Thread、何时使用Service
Service是运行在主线程的,如果我们需要执行耗时操作,也是在Service创建Thread执行。所以我们需要明确一点,Thread和Service不是同一级别的类,Service和Activity才是同一级别的组件,所以这里讨论是应该是Thread应该是在Activity创建还是Service创建的问题。
- 如果耗时操作无法在当前Activity生命周期执行完成的任务就需要在Service执行,比如异步下载等。
- 需要长时间在后台运行,跟随APP生命周期的任务需要在Service执行,比如Socket长连接。
有的人看到这里可能心中有个疑问,创建一个单例模式或者直接new Thread()来执行任务不也是可以实现吗,为何还要创建Service?
确实是如此,如果我们直接new Thread()下载文件,确实是非常方便,假设下载的内容非常的大,可能需要几分钟才能下载完毕,比如下载电影,用户可能无法一直停留在APP,需要退到后台(锁屏、回到桌面或切换到其他APP),如果不使用Service,线程极有可能由于后台管控被暂停了。这个使用必须需要某种手段提高APP的等级,让系统继续为APP分配CPU资源,Service就显得尤为重要。
什么是前台服务,什么是后台服务?
在Android 8.0 推出了新的启动服务方法startForegroundService,不再允许开发者在后台通过startService启动服务,那么问题来了,什么情景下才是后台?
什么是后台?
当前APP的Activity离开手机屏幕(锁屏、回到桌面、切换到其他APP)超过60秒。
什么是前台服务
Android 8.0新增了startForegroundService方法,用于启动前台服务,前台服务是指带有通知栏的服务,如果我们使用startForegroundService启动服务,那么必须在5秒内调用startForeground()
显示一个通知栏,否则就会报错,比如通知栏显示音乐播放器、下载进度。
Android 8.0后是否必须使用 startForegroundService
虽然Android 8.0之后新增了startForegroundService方法,但是startService还是可以正常使用。
实际的案例和线上异常统计,部分Android 8.0手机的startService存在缺陷,虽然APP在前台但是还是会报出
Not allowed to start service Intent
异常,Google Pixel 就可以重现该问题。
SDK中提供的常用服务基类
Service
Service是所有服务的基类,早期我们通常都是继承该类实现服务,如果使用该类,我们需要对Service的生命周期进行管理,在合适的地方停止Service。
IntentService
继承于Service,通过源码可以看到,是在Service的基础上增加了Handler、Looper、HandlerThread的支持,我们只需要重写onHandleIntent(Intent intent)
实现异步任务,这个方法已经是非UI线程,可以执行耗时操作,一旦这个方法执行完毕,就会立刻执行stopSelf()
停止服务,无需手动停止服务。
但是在Android 8.0增加了后台服务限制,并提供了JobScheduler的支持,推荐使用JobIntentService代替IntentService。
JobService
继承于Service,通过源码可以看到,是在Service的基础上增加了JobScheduler的支持,可以非常简单实现多种网络状态的监控。
JobIntentService
继承于Service,带有IntentService和JobService的特性。
LifecycleService
继承于Service,增加了Lifecycle的支持,可以非常方便我们使用LiveData等。
异常情况
异常一
java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.xx.xx/.xxxService }: app is in background uid UidRecord{4dbde56 u0a86 LAST bg:+1m13s953ms idle change:idle procs:1 seq(0,0,0)}
该问题是由于APP在后台调用了startService启动服务导致异常,第一检查APP是否存在后台启动服务的情况,如果存在尽量使用使用startForegroundService启动前台服务(带有通知栏的服务),也可以使用JobService来解决服务问题。
异常二
android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground()
这个是由于使用startForegroundService启动前台服务,但是没有在5秒内调用startForeground显示通知。
异常三
java.lang.SecurityException
Unable to start service Intent { cmp=xx.xx.xx/.XXXService }: Unable to launch app xx.xx.xx/10093 for service Intent { cmp=cmp=xx.xx.xx/.XXXService }: user 0 is restricted
如果这个错误只是在部分的小众手机出现,那么极有可能是由于部分手机厂商修改了Android的源码,增加了安全控制,比如禁止在Application初始化期间startService。