Android 项目 service/worker 定时任务

Android 定时任务

源码

配置

gradle/libs.versions.toml

[versions]
work = "2.11.0"

[libraries]
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }

app/build.gradle.kts

dependencies {
  implementation(libs.androidx.work.runtime.ktx)
}

MinuteTaskService

package cn.com.xuxiaowei.service

import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.text.format.DateFormat
import cn.com.xuxiaowei.utils.FileLogger

class MinuteTaskService : Service() {
    private val handler = Handler(Looper.getMainLooper())
    private val task = object : Runnable {
        override fun run() {
            val now = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis())
            FileLogger.log(
                this@MinuteTaskService,
                "service",
                "MinuteTaskService",
                "每分钟任务执行: $now, uptime=${SystemClock.uptimeMillis()}"
            )
            handler.postDelayed(this, 60_000)
        }
    }

    override fun onCreate() {
        super.onCreate()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        handler.removeCallbacks(task)
        handler.post(task)
        return START_STICKY
    }

    override fun onDestroy() {
        handler.removeCallbacks(task)
        super.onDestroy()
    }

    override fun onBind(intent: Intent?) = null

    companion object {
        fun start(context: Context) {
            context.startService(Intent(context, MinuteTaskService::class.java))
        }
    }
}

FifteenMinuteWorker

package cn.com.xuxiaowei.worker

import android.content.Context
import android.os.SystemClock
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import cn.com.xuxiaowei.utils.FileLogger
import java.util.concurrent.TimeUnit

class FifteenMinuteWorker(
    appContext: Context,
    params: WorkerParameters
) : CoroutineWorker(appContext, params) {
    override suspend fun doWork(): Result {
        val uptime = SystemClock.uptimeMillis()
        FileLogger.log(
            applicationContext,
            "worker",
            "FifteenMinuteWorker",
            "每15分钟任务执行, uptime=$uptime"
        )
        return Result.success()
    }

    companion object {
        private const val UNIQUE_NAME = "cn.com.xuxiaowei.FifteenMinuteWorker"

        fun schedule(context: Context) {
            val request = PeriodicWorkRequestBuilder<FifteenMinuteWorker>(
                15, TimeUnit.MINUTES
            ).build()
            WorkManager.getInstance(context).enqueueUniquePeriodicWork(
                UNIQUE_NAME,
                ExistingPeriodicWorkPolicy.KEEP,
                request
            )
        }
    }
}

App

package cn.com.xuxiaowei

import android.app.Application
import android.os.Build
import cn.com.xuxiaowei.service.MinuteTaskService
import cn.com.xuxiaowei.utils.FileLogger
import cn.com.xuxiaowei.worker.FifteenMinuteWorker

class App : Application() {
    override fun onCreate() {
        super.onCreate()

        val appVersion = try {
            val pm = packageManager
            val pi = pm.getPackageInfo(packageName, 0)
            val versionName = pi.versionName ?: "unknown"
            val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                pi.longVersionCode.toString()
            } else {
                @Suppress("DEPRECATION")
                pi.versionCode.toString()
            }
            "$versionName($versionCode)"
        } catch (e: Exception) {
            "unknown"
        }

        val deviceInfo = "model=${Build.MODEL}, brand=${Build.BRAND}, sdk=${Build.VERSION.SDK_INT}"
        FileLogger.log(this, "startup", "App", "App started version=$appVersion, $deviceInfo")

        MinuteTaskService.start(this)
        FifteenMinuteWorker.schedule(this)
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:name=".App"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Xuxiaowei">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.Xuxiaowei">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".service.MinuteTaskService"
            android:exported="false" />
    </application>

</manifest>

查看日志

  1. 启动日志
  2. service 日志
  3. worker 日志