Android starts the foreground service (startForegroundService)

question:

APP in background in null uid
AndroidRuntime: android.app.RemoteServiceException: Context.startForegroundService()
 did not then call Service.startForeground():

Precautions:

  • 8.0 Adaptation: Notification needs to be added with NotificationChannel, and the way to start the foreground service is startForegroundService()
  • 9.0 Adaptation: Permissions need to be added to the manifest.xml file: FOREGROUND_SERVICE

Detailed explanation of Android's Service service

1. Foreground authority:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

2. Enable notification in 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. Start the 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))
}

How to solve Android O's restrictions on background services

4. Other solutions:

Since Android 8.0 prohibits applications from creating a Service while running in the background, there are several solutions to this problem:

  • Start a foreground Service through Context.startForegroundService(), and the startup of the foreground Service is not restricted.
  • Integrate Google Firebase Messaging.
  • Use JobService;Minimum cycle duration is 15 minutes
  • WorkManager: periodic tasks;Minimum cycle duration is 15 minutes(same as JobScheduler)

The official recommendation is to use JobScheduler to replace the background Service.

From Android 8.0, use JobScheduler to replace Background Service, which periodically starts a task, queries the server, and then exits. Compared with the background Service, it consumes significantly less resources, which indirectly improves the performance of the mobile phone.

Question added:

The minimum interval of JobService must be greater than 15 minutes; otherwise, an error will be reported:

Requested interval +1m0s0ms for job 10 is too small; raising to +15m0s0ms   
Requested flex +1m0s0ms for job 10 is too small; raising to +5m0s0ms        

WorkManager: periodic tasks

5. JobScheduler realizes timing interval processing

The use of Android task scheduling WorkManager and JobSchedule

Solve the requirement that the minimum interval time is greater than 15 minutes in a recursive way;

/**
 * JobScheduler实现定时间隔处理
 * 通过递归的方式,在onStartJob中,利用setMinimumLatency来设置时间间隔,执行完后再重新创建启用任务来实现
 */
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)
        }
    }
}

Need to be reminded:

  • Both JobScheduler and WorkManager can only be executed when the APP is alive, but the timer is always working.
  • After closing the APP and restarting it, JobScheduler cannot continue to run directly, but WorkManager can.
  • If the timer of the WorkManager task should have been executed one or more times when the APP is restarted, it will start executing immediately.
  • If WorkManager directly executes a task after restarting the App, the new cycle will be counted from this point, and the old cycle will not be followed.

Guess you like

Origin blog.csdn.net/zhijiandedaima/article/details/131192337