彻底解决Android 8.0启动服务问题

使用服务首先想到的就是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上对服务的兼容性问题了!!!

重要:使用IntentService,在8.0以上的前台后台服务问题可以解决,但是又出现了新的异常(相对于前后台服务的异常,量级要小很多,但也确实出现了一些异常),且此异常暂时无法根除。而且,JOB_ID不要随意更改,慎重!!!

https://github.com/evernote/android-job/issues/255

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值