彻底解决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
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
Android 8.0 及以上版本,为了增强应用程序的安全性,Android 引入了后台限制,禁止未在前台运行的应用程序启动服务。如果您想在后台启动服务,需要使用 `startForegroundService()` 方法。这个方法会启动一个前台服务,然后你可以在服务启动后在通知栏显示一个通知,以此来告知用户服务正在运行。 以下是一个使用 `startForegroundService()` 的示例代码: ``` if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 创建一个 NotificationChannel NotificationChannel channel = new NotificationChannel("channel_id", "channel_name", NotificationManager.IMPORTANCE_DEFAULT); // 向系统注册 NotificationChannel NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel); } // 创建一个 Intent,启动你的服务 Intent serviceIntent = new Intent(this, YourService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 在 Android 8.0 及以上版本上,需要调用 startForegroundService() 方法启动服务。 startForegroundService(serviceIntent); } else { // 在 Android 8.0 以下版本上,可以直接调用 startService() 方法启动服务。 startService(serviceIntent); } ``` 注意:如果你使用的是 `startForeground()` 方法,会在 Android 8.0 及以上版本上抛出 `IllegalStateException` 异常,因为 Android 8.0 及以上版本禁止在后台启动服务

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值