问题:
APP in background in null uid
AndroidRuntime: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground():
注意事项:
- 8.0适配:通知需要加上NotificationChannel,开启前台服务的方式startForegroundService()
- 9.0适配:manifest.xml文件中需要增加权限:FOREGROUND_SERVICE
1、前台权限:
2、Service中开启通知:
class LogUploadService : Service() { override fun onBind(arg0: Intent): IBinder? { return null } override fun onCreate() { super.onCreate() Log.d("caowj", "LogUploadService onCreate") initNotification() } private fun initNotification() { val channelName = "埋点上传" val channelId = BuildConfig.APPLICATION_ID// 发送通知,把service置于前台 val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // 从Android 8.0开始,需要注册通知通道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH) notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(this, channelId) .setSmallIcon(R.mipmap.app_icon) .setContentTitle("埋点Log上报") .setContentText("服务正在运行,请勿关闭") .setAutoCancel(false) .setOngoing(true) .build() // 注意第一个参数不能为0 startForeground(1, notification) } override fun onDestroy() { //停止的时候销毁前台服务。 stopForeground(true); }}
3、启动Service:
// Android 8.0使用startForegroundService在前台启动新服务if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(Intent(this, LogUploadService::class.java))} else { startService(Intent(this, LogUploadService::class.java))}
4、其他方案:
由于从Android 8.0开始禁止应用在后台运行时创建Service,所以要解决这种这种问题有以下几种方案:
- 通过Context.startForegroundService()方式启动一个前台Service,前台Service的启动没有受到限制。
- 集成Google Firebase Messaging。
- 使用JobService;最小周期时长为 15 分钟
- WorkManager: 周期性任务;最小周期时长为 15 分钟 (与 JobScheduler 相同)
官方建议使用JobScheduler 替换 后台Service。
从Android 8.0,使用JobScheduler替换后台Service,它会周期性启动一个任务,查询服务器,然后退出。相比于后台Service,它消耗的资源明显较少,间接提升了手机性能。
问题补充:
JobService 最小间隔时间要求大于15分钟;否则报错:
Requested interval +1m0s0ms for job 10 is too small; raising to +15m0s0ms Requested flex +1m0s0ms for job 10 is too small; raising to +5m0s0ms
5、JobScheduler实现定时间隔处理
Android之任务调度WorkManager和JobSchedule的使用
通过递归的方式,解决最小间隔时间要求大于15分钟的限制;
class PeriodicJobService : JobService() { override fun onStartJob(p0: JobParameters?): Boolean { Log.i(TAG, "onStartJob---") startScheduler(this) return false } override fun onStopJob(p0: JobParameters?): Boolean = false companion object { var TAG: String = "caowj" var JOBID: Int = 100 var InterValTime: Long = 10000 private var jobScheduler: JobScheduler? = null private var jobInfo: JobInfo? = null fun startScheduler(context: Context) { jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler cancelScheduler() if (jobInfo == null) { jobInfo = JobInfo.Builder(JOBID, ComponentName(context, PeriodicJobService::class.java)) .setMinimumLatency(InterValTime) // 最小为10秒 .build() } val result = jobScheduler?.schedule(jobInfo!!) } fun cancelScheduler() { jobScheduler?.cancel(JOBID) } }}
需要提醒:
- JobScheduler和WorkManager都只能在APP存活的时候执行,但是定时器是一直工作的。
- 关闭APP再启动,JobScheduler并不能够直接继续运行,但是WorkManager可以。
- 如果重启APP的时候,WorkManager任务的计时器应该已经执行了一次或多次,则会立即开始执行。
- 重启App之后WorkManager如果直接执行了一个任务,则从这个时候开始算新的周期,不会按旧有周期走。
来源地址:https://blog.csdn.net/zhijiandedaima/article/details/131192337