更新记录

1.0.3(2026-08-12)

  • Android location 会话默认启用可续期 PARTIAL_WAKE_LOCK,支持 enableWakeLock / wakeLockTimeoutMs 配置。
  • 新增 App 前后台、屏幕亮灭、原生 pulse 三组 watcher(共 6 个 API)及对应状态字段。
  • Android / iOS / HarmonyOS 分别补齐 WakeLock 兜底、生命周期 pulse、RunningLock 与屏幕状态能力。
  • 新增保活诊断(默认关闭):enableDiagnostics 开启后写入应用私有 diagnostics 目录(4×512 KiB JSONL 循环文件);提供 readKeepaliveDiagnostics / clearKeepaliveDiagnostics / exportKeepaliveDiagnostics;Android 导出到 Download/Xview/keepalive/;不含坐标与设备标识,插件不上传。

1.0.2(2026-07-28)

  • 统一异步回调为 success / fail / complete;状态模型改为 state / mode / nativeActive / sceneActive 等。
  • 默认场景 location;未落地场景返回 9030006;错误码扩展到 9030008
  • 新增能力查询、权限检查/申请、配额查询、定位与超时监听、恢复事件确认等 API。
  • 三端只声明真实定位后台能力;Android 支持前台服务、调度兜底与超时锁;iOS / HarmonyOS 对齐真实定位会话。
  • 示例收敛为「权限 → 会话主控 → 通知 / WakeLock / 重连」;启停仅在会话主控页。

1.0.1(2026-07-20)

  • 校正 uni-app 与 uni-app x 的系统授权示例:仅 Android 展示电池优化设置与白名单操作,iOS 展示应用设置,Harmony 展示后台运行指引。
  • 补全基础前台服务示例,展示 isKeepaliveRunning() 与完整状态对象的对照结果。
  • 修复 iOS UTS 包装层对 Swift 原生代码的错误模块导入,兼容 HBuilderX 5.06 编译器。
  • 同步 README 示例页覆盖说明;公开 API、模块路径与包版本保持不变。
查看更多

平台兼容性

uni-app(4.87)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Safari app-vue app-vue插件版本 app-nvue app-nvue插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本
1.0.0 1.0.0 × × 1.0.0 1.0.0 5.0 1.0.0 12 1.0.0 5.0+ 1.0.0
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × × -

uni-app x(5.0)

Chrome Safari Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本 微信小程序
× × 5.0 1.0.0 12 1.0.0 5.0+ 1.0.0 ×

其他

多语言 暗黑模式 宽屏模式
×

XView 跨平台前台、后台保活插件

xview-keepalive 是 Android、iOS、HarmonyOS 的 UTS API 插件,用统一契约管理系统策略允许范围内的长时后台运行。

插件当前只对 location 场景提供完整原生工作链路。connectedDevicemediaPlaybackdataTransfer 作为统一能力名称保留,但没有真实业务引擎时会由原生层返回 9030006,不会通过空任务、静音音频或声明后台类型伪造成功。

后台运行持续时间由系统策略、用户授权、设备电量策略、厂商限制和宿主配置共同决定。插件不承诺永久常驻、用户强退后自启或绝对持续运行。

推荐启动流程

// #ifdef APP
import {
    getKeepaliveCapabilities,
    checkKeepalivePermission,
    requestKeepalivePermission,
    startKeepalive,
    watchKeepaliveLocation,
    watchKeepaliveTimeout,
    XviewKeepaliveCapabilities,
    XviewKeepaliveFail,
    XviewKeepaliveLocation,
    XviewKeepalivePermission,
    XviewKeepaliveStatus,
    XviewKeepaliveTimeoutEvent
} from '@/uni_modules/xview-keepalive'
// #endif

const startLocation = () => {
    watchKeepaliveLocation((location : XviewKeepaliveLocation) => {
        console.log(location.latitude.toString() + ',' + location.longitude.toString())
    })
    watchKeepaliveTimeout((event : XviewKeepaliveTimeoutEvent) => {
        console.log(event.reason + ': ' + event.message)
    })
    startKeepalive({
        scene: 'location',
        title: '后台定位',
        content: '定位任务正在运行',
        resilient: true,
        enableBootRestart: false,
        enableDiagnostics: true,
        enableWakeLock: true,
        wakeLockTimeoutMs: 60000,
        location: {
            accuracy: 'balanced',
            intervalMs: 300000,
            distanceFilter: 100
        },
        success: (status : XviewKeepaliveStatus) => {
            console.log(status.state) // 原生链路确认完成后才可能为 active
        },
        fail: (error : XviewKeepaliveFail) => {
            console.log(error.errCode.toString() + ': ' + error.errMsg)
        }
    })
}

const ensurePermission = () => {
    checkKeepalivePermission({
        scene: 'location',
        success: (permission : XviewKeepalivePermission) => {
            if (permission.granted) {
                startLocation()
                return
            }
            requestKeepalivePermission({
                scene: 'location',
                success: (requested : XviewKeepalivePermission) => {
                    if (requested.granted) {
                        startLocation()
                    }
                }
            })
        }
    })
}

getKeepaliveCapabilities({
    success: (capabilities : XviewKeepaliveCapabilities) => {
        if (capabilities.location.supported && capabilities.location.declared) {
            ensurePermission()
        }
    }
})

停止页面监听时必须显式调用:

stopKeepaliveLocationWatch()
stopKeepaliveTimeoutWatch()

全局状态与 pulse 监听

建议在 App.vue / App.uvue 注册状态与 pulse watcher,而不是只绑定在会被卸载的业务页面。每类 watcher 只有一个 callback,重复注册会替换旧 callback。stopKeepalive() 会停止原生 pulse 调度但保留 watcher;重新启动真实会话后自动恢复,只有对应的 stop...Watch() 会清除 callback。

import {
    watchKeepaliveAppState,
    watchKeepaliveScreenState,
    watchKeepalivePulse,
    XviewKeepaliveAppStateEvent,
    XviewKeepaliveScreenStateEvent,
    XviewKeepalivePulseEvent
} from '@/uni_modules/xview-keepalive'

watchKeepaliveAppState((event : XviewKeepaliveAppStateEvent) => {
    console.log('app=' + event.state + ' source=' + event.source)
})

watchKeepaliveScreenState((event : XviewKeepaliveScreenStateEvent) => {
    console.log('screen=' + event.state + ' supported=' + event.supported.toString())
})

watchKeepalivePulse((event : XviewKeepalivePulseEvent) => {
    // pulse 进入这里表示本次原生调度已经真正进入 DCloud JS Runtime。
    // 可在此调用第三方 JS SDK 获取数据,再交给现有业务接口上传。
    thirdPartySdk.getLatestData().then((data) => {
        uploadBusinessData(data)
    })
    console.log('pulse=' + event.sequence.toString() + ' delay=' + event.delayMs.toString())
}, 10000)

watchKeepalivePulse 最小间隔为 1000ms,只在真实 keepalive 会话运行时派发,不执行第三方脚本本身。原有 setInterval() 可以继续保留作对照:原生 pulse 与 lastNativePulseAt 用于判断原生线程是否仍运行,lastJsPulseDeliveredAt 表示回调是否真正进入 JS。iOS 进程被系统挂起时 pulse 也会暂停;Core Location 回调到达后会补做一次到期检查,但不承诺固定周期。

保活诊断

诊断默认关闭。调用 startKeepalive({ enableDiagnostics: true }) 后,原生层会把开关写入恢复配置;应用冷启动仍需在 App.vue / App.uvue 调用 initializeKeepalive,以恢复诊断会话和内部 JS Runtime 探针。公开 watchKeepalivePulse 的 callback 与间隔不会被诊断探针占用或修改。

import {
    readKeepaliveDiagnostics,
    clearKeepaliveDiagnostics,
    exportKeepaliveDiagnostics,
    XviewKeepaliveDiagnosticsResult
} from '@/uni_modules/xview-keepalive'

readKeepaliveDiagnostics({
    limit: 200,
    success: (result : XviewKeepaliveDiagnosticsResult) => {
        console.log('previous=' + result.previousExitReason)
        console.log('events=' + result.events.length.toString())
    }
})

exportKeepaliveDiagnostics({
    success: (result) => {
        // Android 10+ 的 path 为 content URI;Android 9 及以下为 Download/Xview/keepalive/ 绝对路径。
        console.log(result.path)
    }
})

clearKeepaliveDiagnostics({
    success: (result) => {
        console.log('cleared=' + result.clearedEventCount.toString())
    }
})

诊断存储位于应用私有 diagnostics 目录,使用 4 个 JSONL 循环文件,每个最多 512 KiB,总量约 2 MiB。exportKeepaliveDiagnostics 会先把事件合并为 UTF-8 JSONL,再复制到用户可见目录:Android 写入 Download/Xview/keepalive/(Android 10+ 返回 content URI,Android 9 及以下需 WRITE_EXTERNAL_STORAGE 运行时权限);iOS / HarmonyOS 仍导出到应用沙箱。启停、异常、JS 中断/恢复和关键状态变化立即写入;稳定运行每 60 秒写一次检查点。readKeepaliveDiagnostics 默认读取最新 200 条,limit 范围为 1–1000,事件按时间倒序返回。诊断关闭或暂无历史均返回正常空结果,存储、读取或导出异常才返回 9030009

内部探针每 10 秒尝试进入一次 UTS Runtime;原生检查点仍推进且 30 秒没有探针确认时记录 js_runtime_gap,恢复后记录 js_runtime_recovered。结论按证据区分:系统 API 提供原因时为已确认退出;上一进程标记未正常关闭但没有系统原因时只标记“疑似异常终止”;原生继续推进而 JS 超时只表示“JS Runtime 中断或挂起”;原生和 JS 同时停止且无系统记录时保持 unknown。MetricKit 可能延迟回传,事件中的报告时间与异常时间应分别核对。

日志仅保存进程会话、保活会话、PID、生命周期、保活状态、探针和系统原因摘要,不写入定位坐标、设备唯一标识、完整权限结果或通知正文;系统说明会截断并脱敏。日志不会由插件上传。清空操作保留当前诊断开关,并为仍启用的当前进程重新建立会话记录。

应用启动恢复

App.vueApp.uvueonLaunch 应调用 initializeKeepalive。初始化完成后非破坏性读取恢复事件,先派发给业务,再按事件 ID 确认消费。派发或确认失败时,未确认事件仍保留在原生存储中。

initializeKeepalive({
    success: () => {
        readKeepaliveReconnectEvents({
            success: (result : XviewKeepaliveReconnectEventsResult) => {
                const events : Array<XviewKeepaliveReconnectEvent> = result.events
                const ids : Array<string> = []
                events.forEach((event : XviewKeepaliveReconnectEvent) => {
                    uni.$emit('xview-keepalive:reconnect', event)
                    ids.push(event.id)
                })
                if (ids.length > 0) {
                    confirmKeepaliveReconnectEvents({ ids: ids })
                }
            }
        })
    }
})

Android 只有 enableBootRestart: true 的真实定位任务允许注册开机恢复。调度器被系统触发只会生成恢复事件;只有原生服务和定位会话成功启动后,状态才会变为 active

API

所有异步原生操作使用 success / fail / completecomplete 类型固定为 XviewKeepaliveComplete。除 watcher 外,不再同步返回缓存状态。

API 结果类型 说明
initializeKeepalive(options) XviewKeepaliveStatus 应用启动时恢复原生配置并处理系统唤醒
startKeepalive(options) XviewKeepaliveStatus 启动真实场景引擎
restartKeepalive(options) XviewKeepaliveStatus 读取上次配置并重新启动
stopKeepalive(options) XviewKeepaliveStatus 等待原生资源停止并确认
updateKeepaliveNotification(options) XviewKeepaliveActionResult 更新 Android/Harmony 通知;无等价能力时失败
getKeepaliveStatus(options) XviewKeepaliveStatus 查询权威状态机
isKeepaliveRunning(options) XviewKeepaliveBooleanResult 查询原生场景是否活动
getKeepaliveLimit(options) XviewKeepaliveLimit 查询限制;estimated=true 表示本地估算
getKeepaliveCapabilities(options) XviewKeepaliveCapabilities 查询场景和系统辅助能力
checkKeepalivePermission(options) XviewKeepalivePermission 查询场景权限
requestKeepalivePermission(options) XviewKeepalivePermission 分阶段申请场景权限
requestNotificationPermission(options) XviewKeepalivePermission 申请并返回真实通知权限
watchKeepaliveLocation(callback) void 注册保活定位回调
stopKeepaliveLocationWatch() void 释放定位回调
watchKeepaliveTimeout(callback) void 注册系统超时、权限撤销等事件
stopKeepaliveTimeoutWatch() void 释放 timeout 回调
watchKeepaliveAppState(callback) void 立即返回并持续监听前后台状态
stopKeepaliveAppStateWatch() void 释放前后台状态 callback
watchKeepaliveScreenState(callback) void 立即返回屏幕状态;iOS 明确返回 unsupported
stopKeepaliveScreenStateWatch() void 释放屏幕状态 callback
watchKeepalivePulse(callback, intervalMs) void 注册原生 pulse;最小间隔 1000ms
stopKeepalivePulseWatch() void 清除 pulse callback 并停止当前调度
isNotificationEnabled(options) XviewKeepaliveBooleanResult 查询通知授权
openNotificationSettings(options) XviewKeepaliveActionResult 打开原生通知设置
isIgnoringBatteryOptimizations(options) XviewKeepaliveBooleanResult 查询 Android 电池优化状态
requestIgnoreBatteryOptimizations(options) XviewKeepaliveActionResult 请求 Android 忽略电池优化
openBatteryOptimizationSettings(options) XviewKeepaliveActionResult 打开 Android 电池优化设置
startWakeLock(options) XviewKeepaliveActionResult Android 申请 WakeLock;Harmony 按 RunningLock 探测结果执行
readKeepaliveReconnectEvents(options) XviewKeepaliveReconnectEventsResult 非破坏性读取原生持久化恢复事件,事件位于 events 字段
confirmKeepaliveReconnectEvents(options) XviewKeepaliveActionResult 按 ID 确认并删除已派发的恢复事件
addKeepaliveReconnectEvent(options) XviewKeepaliveReconnectEvent 主动写入原生持久化恢复事件
readKeepaliveDiagnostics(options) XviewKeepaliveDiagnosticsResult 读取最新 1–1000 条原生诊断事件和上一进程摘要
clearKeepaliveDiagnostics(options) XviewKeepaliveDiagnosticActionResult 清空历史并保留当前诊断开关、重建当前会话记录
exportKeepaliveDiagnostics(options) XviewKeepaliveDiagnosticExportResult 导出 UTF-8 JSONL;Android 复制到 Download/Xview/keepalive/

状态模型

XviewKeepaliveStatus 的核心字段:

  • statestopped / starting / active / degraded / stopping / failed
  • modecontinuous / transient / none
  • nativeActive:平台后台能力是否已由原生层确认启动。
  • sceneActive:真实场景引擎是否活动。
  • degraded:当前运行质量是否退化。
  • notificationVisible:用户可感知通知是否真实展示。
  • locationActive:原生定位会话是否真实活动。
  • appState / screenState / screenStateSupported:当前前后台与亮灭屏状态及平台支持语义。
  • foregroundServiceActive:Android 同进程前台服务真实实例是否存在;iOS/Harmony 固定为 false。
  • lastNativePulseAt:原生计时器最近一次产生 pulse 的墙钟时间。
  • lastJsPulseDeliveredAt:UTS callback 最近一次真正进入 JS Runtime 的墙钟时间。
  • diagnosticsEnabled:原生诊断持久化是否开启。
  • diagnosticJsProbeActive:独立内部 JS Runtime 探针是否已注册。
  • diagnosticLastPersistedAt:最近一次诊断事件成功落盘的时间戳,尚未落盘时为 0
  • wakeLockHeld:Android WakeLock 或 Harmony RunningLock 的真实持有状态。

state === 'active' && nativeActive && sceneActive 表示完整链路运行;degraded 表示真实 location 会话仍可能运行,但通知或锁等增强条件不完整。插件不会把 degraded 伪装成完整成功。

错误码

错误码 说明
9030001 当前平台没有等价原生操作
9030002 原生启动链路失败
9030003 原生停止链路失败
9030004 打开系统设置失败
9030005 参数或场景名称不合法
9030006 场景尚未接入真实原生工作负载
9030007 系统时长配额已耗尽
9030008 缺少场景必需权限
9030009 诊断存储、读取或导出失败

宿主配置

Android

插件仅声明 location 前台服务类型,服务不配置独立 android:process,与 DCloud JS Runtime 位于应用主进程。目标版本较高时需要按系统要求分阶段处理通知、前台定位和后台定位权限。若宿主修改或合并 Manifest,必须保留插件的 service、receiver、job service 和真实定位权限声明。 华为/vivo 等系统还必须按上文开启厂商后台运行权限;Android 电池优化白名单与厂商“应用启动管理 / 后台高耗电”是两套独立策略,只配置其中一项不能保证 WakeLock 不被厂商代理释放。 Android 11(API 30)及以上会读取上一进程的 ApplicationExitInfo 精简原因与 process-state 摘要;较低版本只有本地会话证据。系统记录也可能受设备实现和历史保留数量限制。

iOS

插件只声明 UIBackgroundModes = location。宿主必须配置符合实际用途的定位权限文案,并对 App Store 审核解释真实后台定位业务。用户主动强退、关闭后台 App 刷新或撤销 Always 权限时,系统可能阻止恢复。 iOS 13 及以上注册 MetricKit subscriber;MXDiagnosticPayload 由 iOS 14 起提供,因此 iOS 14+ 才能接收系统诊断载荷,且报告可能在异常发生后延迟送达。iOS 12–13 以本地生命周期、定位、内存警告和会话标记为主。willTerminate 仅作为线索,不作为正常退出的唯一依据。

HarmonyOS

宿主需要声明 KEEP_BACKGROUND_RUNNINGLOCATIONAPPROXIMATELY_LOCATIONLOCATION_IN_BACKGROUNDRUNNING_LOCK,Ability 的 backgroundModes 只包含 locationBACKGROUND RunningLock 已被系统标记 deprecated 且没有公开等价替代,插件会先探测设备能力和权限;不可用或申请失败时继续保留真实 LOCATION 连续任务,但状态返回 degradedwakeLockHeld=false 和具体原因。插件从 UIAbilityContext 动态读取 bundle、module 和 ability 信息,不依赖示例包名。 HarmonyOS 不引入 AGC 或网络依赖,应用内仅记录 Ability 生命周期、持续任务、定位、RunningLock、屏幕、状态与探针证据;精确 AppKilled/Freeze 原因需将导出 JSONL 的时间戳与 DevEco FaultLog 对照。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

Android: FOREGROUND_SERVICE, FOREGROUND_SERVICE_LOCATION, ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION, POST_NOTIFICATIONS, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, WAKE_LOCK, ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, RECEIVE_BOOT_COMPLETED; iOS: UIBackgroundModes location; HarmonyOS: ohos.permission.KEEP_BACKGROUND_RUNNING, LOCATION, APPROXIMATELY_LOCATION, LOCATION_IN_BACKGROUND, RUNNING_LOCK 需在主工程配置。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不上传或对外采集数据;仅在调用方显式启用诊断时,在应用私有目录保存脱敏的保活状态日志,并支持应用内清除和导出。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: