You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin开发习惯跟踪应用如何在每日午夜更新数据库与RecyclerView

解决方案

一、前置优化:补充DAO层必要方法

你现有DAO缺少批量插入、按日期查询、去重查习惯模板的能力,先修改HabitDao.kt:

@Dao
interface HabitDao {
    @Query("SELECT * FROM habititem")
    suspend fun getAll(): List<HabitItem>

    // 按日期查询当日所有习惯
    @Query("SELECT * FROM habititem WHERE date = :targetDate")
    suspend fun getByDate(targetDate: String): List<HabitItem>

    // 查询所有去重的习惯模板(名称+描述)
    @Query("SELECT DISTINCT name, description FROM habititem")
    suspend fun getAllHabitTemplates(): List<HabitTemplate>

    @Insert
    suspend fun insert(habitItem: HabitItem): Long

    // 批量插入新日期的习惯条目
    @Insert
    suspend fun insertBatch(vararg habitItems: HabitItem)

    @Update
    suspend fun update(habitItem: HabitItem)

    @Delete
    suspend fun deleteItem(habitItem: HabitItem)
}

// 用于接收去重的习惯模板
data class HabitTemplate(
    val name: String,
    val description: String
)

如果需要更严谨的逻辑,建议单独建一张HabitTemplate表存储用户创建的习惯(含是否启用状态),避免把用户已删除的历史习惯重新生成出来。

二、使用WorkManager实现午夜定时触发

这是Android官方推荐的后台定时任务方案,适配全系统版本,省电且稳定性优于AlarmManager。

2.1 引入依赖

在module级build.gradle.kts中添加WorkManager依赖:

dependencies {
    implementation("androidx.work:work-runtime-ktx:2.8.1")
}

2.2 实现后台任务Worker

新建HabitDailyInsertWorker.kt,核心逻辑为生成新日期的未勾选习惯条目并插入数据库:

import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import java.text.SimpleDateFormat
import java.util.*

class HabitDailyInsertWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {
    private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
    private val db = HabitListDatabase.getDatabase(context)

    override suspend fun doWork(): Result {
        return try {
            val today = dateFormat.format(Date())
            // 先判断今日是否已经生成过条目,避免重复插入
            val existingItems = db.habitItemDao().getByDate(today)
            if (existingItems.isNotEmpty()) return Result.success()

            // 获取所有习惯模板,生成今日的未勾选条目
            val templates = db.habitItemDao().getAllHabitTemplates()
            val newItems = templates.map { template ->
                HabitItem(
                    name = template.name,
                    description = template.description,
                    date = today,
                    isDone = false
                )
            }
            db.habitItemDao().insertBatch(*newItems.toTypedArray())
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
}

2.3 注册周期性定时任务

新建自定义Application类,在APP启动时注册定时任务:

import android.app.Application
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import java.util.Calendar
import java.util.concurrent.TimeUnit

class HabitApp : Application() {
    override fun onCreate() {
        super.onCreate()
        setupDailyWorker()
    }

    private fun setupDailyWorker() {
        // 计算到下一个午夜的延迟时间
        val currentTime = Calendar.getInstance()
        val nextMidnight = Calendar.getInstance().apply {
            set(Calendar.HOUR_OF_DAY, 0)
            set(Calendar.MINUTE, 0)
            set(Calendar.SECOND, 0)
            add(Calendar.DAY_OF_MONTH, 1)
        }
        val initialDelay = nextMidnight.timeInMillis - currentTime.timeInMillis

        // 构建周期为1天的任务
        val dailyWorkRequest = PeriodicWorkRequestBuilder<HabitDailyInsertWorker>(
            repeatInterval = 1,
            repeatIntervalTimeUnit = TimeUnit.DAYS,
            flexTimeInterval = 15,
            flexTimeIntervalUnit = TimeUnit.MINUTES
        ).setInitialDelay(initialDelay, TimeUnit.MILLISECONDS)
            .build()

        // 注册任务,避免重复注册
        WorkManager.getInstance(this)
            .enqueueUniquePeriodicWork(
                "HabitDailyInsert",
                androidx.work.ExistingPeriodicWorkPolicy.KEEP,
                dailyWorkRequest
            )
    }
}

在AndroidManifest.xml的application标签中添加android:name=".HabitApp"注册自定义Application。

2.4 添加必要权限

在AndroidManifest.xml中添加权限,保证设备重启后定时任务依然生效:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android13+需要 -->

三、边缘场景补充

如果用户多日未打开APP,定时任务不会触发,建议在每日视图页面的onCreate/onResume生命周期中,先检查当前日期是否有对应的习惯条目,如果没有则直接调用一次和Worker相同的插入逻辑,避免数据缺失。

内容的提问来源于stack exchange,提问作者csl15

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 19:24:04