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

Android设备闲置时定时器失效问题及适配方案咨询

Android锁屏后定时任务停止、亮屏批量执行的解决方案

问题背景

开发Android应用时需每隔固定间隔(原需求为100ms)播放声音,要求设备闲置(锁屏)时也能正常运行。当前现象:屏幕熄灭短时间内功能正常,锁屏约1分钟后定时器停止工作,点亮屏幕后错过的任务会连续执行。

已尝试方案

1. 后台服务

确认后台服务已启用,亮屏时功能正常,但锁屏后失效。
后台服务代码:

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    timer.scheduleAtFixedRate(TimeTask(), 0, 100);
    return START_STICKY
}

private inner class TimeTask() : TimerTask() {
     override fun run() {
        sendBroadcast(Intent(TIMER_UPDATED))
     }
}

2. 前台服务

将服务改为前台服务后,问题依旧。
前台服务代码:

private fun runInBackground() {
        val channelId =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                createNotificationChannel("service", "something")
            } else {
                ""
            }

        val notification: Notification = NotificationCompat.Builder(this, channelId)
            .setContentTitle("title")
            .setContentText("text")
            .setSmallIcon(R.drawable.alert_dark_frame)
            .build()

        startForeground(1, notification)
}

@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String{
        val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE)
        channel.lightColor = Color.BLUE
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
            .createNotificationChannel(channel)
        return channelId
}

3. 任务调度的困惑

明确AlarmManager不适合100ms级高频任务(官方文档不推荐),尝试用Handler替换Timer但失败,错误代码如下:

val updateHandler = Handler()

val runnable = Runnable {
    // some action
}

updateHandler.looper(runnable, 100)

解决方案

核心原因

Android设备锁屏进入休眠后,会降低后台进程的CPU调度优先级,甚至暂停非关键线程执行。Timer和绑定主线程/普通后台线程的Handler会被挂起,导致任务堆积,亮屏后一次性执行。

正确实现方式

1. 带唤醒锁的HandlerThread方案

创建独立HandlerThread并获取PARTIAL_WAKE_LOCK,确保CPU在锁屏时仍能运行线程:

class SoundTimerService : Service() {
    private lateinit var wakeLock: PowerManager.WakeLock
    private lateinit var handlerThread: HandlerThread
    private lateinit var updateHandler: Handler

    override fun onCreate() {
        super.onCreate()
        // 获取部分唤醒锁,仅保持CPU运行,不影响屏幕状态
        val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
        wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SoundTimer:WakeLock")
        wakeLock.acquire()

        // 创建独立线程承载Handler,避免阻塞主线程
        handlerThread = HandlerThread("SoundTimerThread")
        handlerThread.start()
        updateHandler = Handler(handlerThread.looper)

        // 启动循环任务
        startRepeatingTask()
    }

    private fun startRepeatingTask() {
        val runnable = object : Runnable {
            override fun run() {
                // 执行播放声音逻辑
                sendBroadcast(Intent(TIMER_UPDATED))
                // 延迟100ms后再次执行
                updateHandler.postDelayed(this, 100)
            }
        }
        updateHandler.post(runnable)
    }

    override fun onDestroy() {
        super.onDestroy()
        // 释放所有资源,避免内存泄漏
        updateHandler.removeCallbacksAndMessages(null)
        handlerThread.quitSafely()
        if (wakeLock.isHeld) {
            wakeLock.release()
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }
}

2. 权限声明

在AndroidManifest.xml中添加唤醒锁权限:

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

3. 优化前台服务(适配高版本Android)

Android 8.0及以上建议保留前台服务,且避免将通知渠道设为IMPORTANCE_NONE(可能被系统忽略):

@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String{
    val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW)
    channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
    (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
    return channelId
}

关键说明

  • PARTIAL_WAKE_LOCK:仅保持CPU运行,功耗较低,符合闲置设备运行的需求。
  • HandlerThread:独立线程保证任务稳定循环,避免阻塞主线程。
  • 弃用Timer:Timer依赖系统时钟,休眠时无法保证执行,Handler的postDelayed更可靠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 02:30:46