使用服务首先想到的就是Service,然后在8.0上做兼容处理。
按理说做完以上可以正常功能,没问题,但是因需求原因,发现还是会出现异常:
-
android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground()
android.app.ActivityThread$H.handleMessage(ActivityThread.java:2204)
刚开始没有头绪,后来发现这个错误越来越多,但是仍然没有找到问题所在。因为直觉告诉是服务在8.0系统的兼容问题。但是明明已经做了这个兼容处理啊!!!
看看服务类:
class AlarmService: Service() {
private val MSG_TYPE_HANDLE_RESIDENT_NOTIFICATION = 0
private val FOREGROUND_SERVICE_ID = 1
private val mHandler: Handler = object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg == null) {
return
}
when(msg.what) {
MSG_TYPE_HANDLE_RESIDENT_NOTIFICATION -> {
//刷新常驻通知栏
NotificationUtil.showResidentNotification(CommonManager.getBaseContext())
WidgetManager.refreshAllWidget(this@AlarmService)
//定时发送通知广播,然后启动该service执行任务
NotificationUtil.setAlertAlarm()
}
else -> {}
}
}
}
companion object {
@JvmStatic
fun startAlarmService(context: Context?) {
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.setClass(context, AlarmService::class.java)
AppUtils.startForegroundServiceSafety(context, intent) //兼容8.0之前 之后启动服务
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
Trace.e("onVisibleToUser", "启动服务: ${System.currentTimeMillis()}")
dealWithForegroundService()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Trace.e("onVisibleToUser", "onStartCommand")
dealWithForegroundService()
val msg = mHandler.obtainMessage()
msg.what = MSG_TYPE_HANDLE_RESIDENT_NOTIFICATION
if (intent != null) {
msg.obj = intent.action
}
mHandler.sendMessage(msg)
return super.onStartCommand(intent, flags, startId)
}
/**
* 说明:处理8.0以上服务---8.0以后在后台启动服务会导致app崩溃,故启动方式需要兼容8.0,同时需要将服务变为前置
*/
private fun dealWithForegroundService() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
val channel = NotificationChannel("app_alarm_service", getString(R.string.app_name), NotificationManager.IMPORTANCE_MIN)
notificationManager?.createNotificationChannel(channel)
val notification = Notification.Builder(applicationContext, "app_alarm_service")
.setContentTitle(getString(R.string.app_name))
.setContentText("正在更新...")
.build()
Trace.e("onVisibleToUser", "变为前台服务")
startForeground(FOREGROUND_SERVICE_ID, notification) //可以保证兼容8.0在后台启动服务
stopForeground(true) //保证兼容后不让通知栏显示,即不让用户在通知栏看到“xxx正在运行 触摸即可了解详情或停止应用”
}
}
override fun onDestroy() {
super.onDestroy()
}
}
其中,启动服务的方法
@JvmStatic fun startForegroundServiceSafety(context: Context?, intent: Intent?) {
context?.runSafety {intent?.let {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
context.startForegroundService(it)
} else {
context.startService(it)
}
}}
}
是吧,明明已经处理了8.0的兼容问题,但是这个错依然频繁出现!!!
那就可能是:因为是在首页启动的服务,那么有可能是有的手机启动app比较卡,导致5s内没有回调startForeground,最终导致了崩溃。
偶然一次,在测试机魅族M1816上重现了该异常,启动到首页确实卡,也确实崩了。最后发现还有一个因素是:因为是定时启动该服务,执行操作,当我把时间设置为每2s执行一次时,那么势必会频繁启动该Service,启动了几次之后也出现了崩溃。但是在非8.0系统的手机上则不会崩溃。
结论:出现 android.app.RemoteServiceException的根本原因还是8.0系统中服务的兼容性。
那么现在说解决,发现通过寻常的Service无法彻底解决该问题,那怎么办呢?
最后找到了一种替代方案:使用 JobIntentService 替代 传统的Service。
经过验证:问题得到解决!!!
class NotifyAlarmService: JobIntentService() {
private val MSG_TYPE_HANDLE_RESIDENT_NOTIFICATION = 123
private val mHandler: Handler = object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg == null) {
return
}
when(msg.what) {
MSG_TYPE_HANDLE_RESIDENT_NOTIFICATION -> {
//刷新常驻通知栏
NotificationUtil.showResidentNotification(CommonManager.getBaseContext())
WidgetManager.refreshAllWidget(this@NotifyAlarmService)
//定时发送通知广播,然后启动该service执行任务
NotificationUtil.setAlertAlarm()
}
else -> {}
}
}
}
companion object {
private const val JOB_ID = 1000
@JvmStatic
fun enqueueWork(context: Context?) {
val intent = Intent()
context?.let {
intent.setClass(context, NotifyAlarmService::class.java)
enqueueWork(context, NotifyAlarmService::class.java, JOB_ID, intent)
}
}
}
override fun onHandleWork(intent: Intent) {
Trace.e("NotifyAlarmService", "onHandleWork")
val msg = mHandler.obtainMessage()
msg.what = MSG_TYPE_HANDLE_RESIDENT_NOTIFICATION
if (intent != null) {
msg.obj = intent.action
}
mHandler.sendMessage(msg)
}
}
这样,就不用再担心会出现8.0上对服务的兼容性问题了!!!