彻底解决Android(安卓)8.0启动服务问题
16lz
2021-01-25
使用服务首先想到的就是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,同时需要将服务变为前置 * 作者:耿保龙 * 添加时间:2020/6/30 14:48 * 修改人:耿保龙 * 修改时间:2020/6/30 14:48 */ 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上对服务的兼容性问题了!!!
更多相关文章
- Android点击通知栏信息后返回正在运行的程序,而不是一个新Activit
- Android应用启动白屏问题
- Android(安卓)Looper(cpp)用法举例
- GLES2.0 on Android(安卓)emulator
- 【Android开发bug】Dropping event due to no window focus
- Android(安卓)4.2 原生系统有哪些方法实现全屏下隐藏导航栏?
- Android(安卓)Permission权限通知
- Android中文API(145) —— NotificationManager
- Android全屏显示(隐藏项目名和通知栏)