HarmonyOS 保持屏幕常亮与后台持锁:setWindowKeepScreenOn 与 runningLock 怎么选
HarmonyOS 保持屏幕常亮与后台持锁:setWindowKeepScreenOn 与 runningLock 怎么选
前言
在华为开发者论坛,一个高频问题是「鸿蒙系统如何设置禁止休眠(熄屏)」。很多开发者第一反应是去找一个全局开关,希望自己的应用一启动,整台手机就永不熄屏。这种预期需要首先被纠正:第三方应用并没有能力真正接管系统的熄屏策略。
系统何时熄屏,由电源管理(Power Manager)统一决策,受用户设置、电池策略、温控等多重因素约束。应用能做且只做两件事:第一,在自身处于前台时,请求「当前窗口保持常亮」;第二,在退到后台仍需持续工作时,通过后台长时任务 + 持锁机制让进程不被回收,但屏幕该熄屏仍然会熄屏。本文把这两条路径讲清,并给出可直接落地的选型与代码。
问题描述
原帖诉求是「禁止系统休眠」。把它翻译成工程语言,实际落地时通常只有三种具体场景:
- 用户正在看阅读页、导航页、视频页时,不要中途灭屏——这是前台常亮需求。
- 应用退到后台后,例如导航语音播报、运动轨迹记录,仍需持续运行一段时间——这是后台保活需求,但屏幕此时本就该灭。
- 自动化测试或演示时希望设备不锁屏——这属于调试手段,不应写进应用逻辑。
把「禁止系统休眠」这一个大而模糊的需求,拆成「前台常亮」与「后台持锁」两个正交的能力,是正确实现的第一步。混淆二者,要么导致常亮忘关持续吃电,要么后台任务违规被系统冻结。
细节解析
核心机制有两套:@ohos.window 的 setWindowKeepScreenOn 解决前台常亮;@ohos.runningLock 配合 backgroundTaskManager 解决后台保活。二者作用域完全不重叠,对比如下:
| 方案 | 作用范围 | 是否需权限 | 适用场景 | 被系统冻结风险 |
|---|---|---|---|---|
setWindowKeepScreenOn(true) | 仅本应用处于前台的窗口 | 否,免权限免申请 | 阅读、导航、视频等前台常亮 | 无;但持续点亮屏幕会显著耗电 |
runningLock + 后台长时任务 | 应用退到后台仍持锁运行 | 需声明 ohos.permission.KEEP_BACKGROUND_RUNNING | 导航后台播报、数据同步、轨迹记录 | 有;超时或滥用约 1 分钟后被冻结 |
@ohos.screenLock(MDM) | 设备级锁屏控制 | 需设备管理(MDM)能力 | 企业设备管控类应用 | 无,但普通应用无此权限,不可用 |
关于 runningLock 的锁类型,需注意枚举命名以本机 SDK 的 RunningLockType 枚举为准,常见类型包括 BACKGROUND(阻止系统挂起 CPU/进程)、PROXIMITY_SCREEN_OFF(接近传感器熄屏)、接近点亮等,具体常量名请以你工程当前 SDK 的 API 文档为准,不要硬编码猜测的名字。
后台长时任务有硬性约束:必须在 module.json5 声明 backgroundModes(如 dataTransfer、location、audioPlayback),并调用 backgroundTaskManager.startBackgroundRunning()。系统对后台任务有超时上限,超时或长时间无有效进展会被判定为 THREAD_BLOCK 进而冻结应用,因此任务完成必须及时 stopBackgroundRunning()。
示例代码
路径 A:页面级前台常亮封装,正确使用生命周期开关。
import { window } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
export class ScreenKeepOnHelper {
private win: window.Window | null = null;
async enable(): Promise<void> {
try {
const context: common.UIAbilityContext = getContext() as common.UIAbilityContext;
this.win = await window.getLastWindow(context);
await this.win.setWindowKeepScreenOn(true);
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`enable keep screen on failed, code: ${err.code}, message: ${err.message}`);
}
}
async disable(): Promise<void> {
if (this.win === null) {
return;
}
try {
await this.win.setWindowKeepScreenOn(false);
this.win = null;
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`disable keep screen on failed, code: ${err.code}, message: ${err.message}`);
}
}
}
在页面中调用,务必在 onPageHide 与 onBackPress 关闭,避免持续耗电:
import { ScreenKeepOnHelper } from '../utils/ScreenKeepOnHelper';
@Entry
@Component
struct ReadingPage {
private helper: ScreenKeepOnHelper = new ScreenKeepOnHelper();
onPageShow(): void {
this.helper.enable();
}
onPageHide(): void {
this.helper.disable();
}
onBackPress(): boolean {
this.helper.disable();
return false;
}
build() {
Column() {
Text('阅读内容区域')
.fontSize(20)
}
.width('100%')
.height('100%')
}
}
路径 B:后台长时任务 + 持锁完整示例。
import { runningLock } from '@kit.BackgroundTasksKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
export class KeepAliveManager {
private lock: runningLock.RunningLock | null = null;
async startLongTask(): Promise<void> {
const context: common.UIAbilityContext = getContext() as common.UIAbilityContext;
try {
await backgroundTaskManager.startBackgroundRunning(
context,
backgroundTaskManager.BackgroundMode.DATA_TRANSFER,
{ abilityName: 'EntryAbility', bundleName: context.abilityInfo.bundleName }
);
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`startBackgroundRunning failed, code: ${err.code}, message: ${err.message}`);
return;
}
try {
this.lock = await runningLock.create('my_running_lock', runningLock.RunningLockType.BACKGROUND);
this.lock.lock(runningLock.RunningLockType.BACKGROUND, 60000);
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`create running lock failed, code: ${err.code}, message: ${err.message}`);
}
}
async stopLongTask(): Promise<void> {
const context: common.UIAbilityContext = getContext() as common.UIAbilityContext;
if (this.lock !== null) {
this.lock.unlock();
this.lock = null;
}
try {
await backgroundTaskManager.stopBackgroundRunning(context);
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`stopBackgroundRunning failed, code: ${err.code}, message: ${err.message}`);
}
}
}
module.json5 权限与后台模式配置片段:
{
"module": {
"name": "entry",
"type": "entry",
"requestPermissions": [
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "$string:keep_bg_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
],
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ts",
"backgroundModes": [
"dataTransfer",
"location"
]
}
]
}
}
调试场景下防熄屏,无需写应用逻辑:执行 hdc shell power-shell wakeup 临时唤醒,或在「开发者选项」开启「充电时不锁定屏幕」即可,更适合自动化测试。
总结
「禁止系统休眠」在第三方应用层面不成立。正确做法是按场景选型:纯前台常亮用 setWindowKeepScreenOn,并在 onPageHide/onBackPress 及时关闭;后台持续运行用 runningLock + backgroundTaskManager 长时任务,声明 backgroundModes 与 KEEP_BACKGROUND_RUNNING 权限,并在任务结束立即 stopBackgroundRunning 规避冻结;@ohos.screenLock 属 MDM 能力,普通应用不可用。把前台常亮与后台持锁两条正交路径分清,才能既满足体验又不踩电源策略与后台管控的红线。
更多推荐



所有评论(0)