更新记录
1.0.1(2026-11-03)
更新使用案例
1.0.0(2026-11-03)
- 首次发布
- Android:前台服务保活(常驻通知)、WakeLock 保持 CPU 唤醒、电池优化白名单申请
- iOS:屏幕常亮(idleTimerDisabled)、短时后台任务(beginBackgroundTask)、通知权限检测与申请
- 两端统一 API:checkPermissions / requestPermissions / startKeepAlive / stopKeepAlive / isKeepAliveRunning
- 支持 Android / iOS 条件编译适配
平台兼容性
uni-app(5.0)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-nvue | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | - | - | - | - | 5.0 | 1.0.0 | 12 | 1.0.0 | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.0)
| Chrome | Safari | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|---|
| - | - | 5.0 | 1.0.0 | 12 | 1.0.0 | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | × | √ |
applive-keepalive 后台保活插件
基于 UTS 开发的 uni-app / uni-app x 通用后台保活与权限管理插件,支持 Android 和 iOS 两端,通过条件编译自动适配。
一、功能概述
| 能力 | Android | iOS |
|---|---|---|
| 后台保活 | ✅ 前台服务 + WakeLock + 电池优化白名单 | ⚠️ 屏幕常亮 + 短时后台任务(受系统限制) |
| 权限检测 | ✅ 同步检测 | ✅ 异步检测 |
| 权限申请 | ✅ 通知权限(13+)、电池优化跳转 | ✅ 通知权限 |
| 条件编译适配 | ✅ | ✅ |
重要提示:iOS 系统严格限制后台运行,不存在与 Android 等价的无限制后台保活。iOS 仅允许声明并真实使用特定后台能力(音频/定位/VoIP)时继续运行。详见下文「iOS 后台保活说明」。
二、插件目录结构
uni_modules/applive-keepalive/
├── package.json # 插件元信息
├── changelog.md
├── readme.md
└── utssdk/
├── app-android/
│ ├── index.uts # Android 实现(前台服务 + WakeLock + 权限)
│ ├── config.json # minSdkVersion
│ └── AndroidManifest.xml # 权限声明 + Service 注册
└── app-ios/
├── index.uts # iOS 实现(屏幕常亮 + 后台任务 + 通知权限)
├── config.json # deploymentTarget
└── Info.plist # UIBackgroundModes 后台模式配置
三、API 说明
两端 API 命名完全一致,JS 层可直接调用,无需区分平台。
1. checkPermissions(callback)
检测权限状态。
import { checkPermissions } from '@/uni_modules/applive-keepalive';
checkPermissions((res) => {
// res = {
// code: 0, // 0 成功
// platform: 'android' | 'ios',
// notification: 'granted' | 'denied' | 'notDetermined',
// ignoreBatteryOptimization: boolean, // Android 电池优化白名单
// foregroundServiceSupported: boolean // Android 前台服务支持
// }
console.log(res);
});
2. requestPermissions(callback)
申请保活所需权限。
import { requestPermissions } from '@/uni_modules/applive-keepalive';
requestPermissions((res) => {
console.log(res.message); // 权限申请完成 / 用户拒绝...
});
3. startKeepAlive(options)
启动后台保活。
import { startKeepAlive } from '@/uni_modules/applive-keepalive';
const res = startKeepAlive({
title: '应用保活中', // 通知标题
content: '正在后台运行' // 通知内容
});
console.log(res.message);
4. stopKeepAlive()
停止后台保活。
import { stopKeepAlive } from '@/uni_modules/applive-keepalive';
const res = stopKeepAlive();
5. isKeepAliveRunning()
判断保活是否运行中,返回 boolean。
6. requestIgnoreBatteryOptimization()(仅 Android)
跳转系统设置申请电池优化白名单,返回 { code, message }。
7. isIgnoringBatteryOptimization()(仅 Android)
是否已加入电池优化白名单,返回 boolean。
8. getKeepAliveInfo()
获取保活服务运行信息,用于验证保活是否生效,返回:
{
running: true, // 服务是否运行中
startTime: 1699000000000, // 服务启动时间戳(毫秒),未运行时为 0
uptime: 125.6 // 已运行秒数,未运行时为 0
}
9. goBackground()(仅 Android 有效)
模拟按 Home 键让 App 退到后台,方便测试保活效果,返回 { code, message }。iOS 端请手动按 Home 键。
三·补 如何验证保活是否生效
- 调用
startKeepAlive()启动保活,getKeepAliveInfo().startTime会得到一个时间戳 T0。 - 按 Home 键退到后台(Android 可调用
goBackground()),等待 30 秒~2 分钟。 - 重新打开 App,调用
getKeepAliveInfo()观察:running仍为 true 且startTime未变、uptime持续增长 → 保活生效,进程在后台一直存活;startTime变了(uptime 归零重计) → 服务曾被系统杀死后自动重启(START_STICKY生效);running为 false → 保活失败,检查通知权限 / 电池优化白名单 / 厂商后台限制。
测试页 pages/index/index.vue 已内置以上逻辑(每秒轮询显示运行时长 + 一键退后台按钮)。
四、Android 保活原理
Android 端采用前台服务(Foreground Service)作为核心保活手段,这是 Android 8.0+ 最可靠的合法保活方式:
- 前台服务 + 常驻通知:进程优先级提升到「前台」,系统基本不杀。
- WakeLock(PARTIAL_WAKE_LOCK):保持 CPU 唤醒,防止息屏后 CPU 休眠。
- 电池优化白名单:加入后系统省电策略不会杀掉进程。
- START_STICKY:服务被系统杀死后尝试用原 Intent 自动重启。
Android 需要处理的权限
| 权限 | 类型 | 说明 |
|---|---|---|
FOREGROUND_SERVICE |
清单声明 | 已在插件 AndroidManifest.xml 声明,无需运行时申请 |
FOREGROUND_SERVICE_DATA_SYNC |
清单声明 | Android 14+ 前台服务类型,已声明 |
WAKE_LOCK |
清单声明 | 已声明 |
POST_NOTIFICATIONS |
运行时申请 | Android 13+,通过 requestPermissions() 申请 |
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS |
跳转设置 | 通过 requestIgnoreBatteryOptimization() 引导用户开启 |
厂商适配注意
国产 ROM(小米/华为/OPPO/vivo 等)有额外的后台管理,建议引导用户:
- 关闭该应用的「省电策略限制」
- 加入「自启动白名单」
- 开启「允许后台运行」
这些无法通过代码完全自动化,需要用户手动在系统设置中操作。
五、iOS 后台保活说明
现实约束
iOS 对后台运行限制严格,App 退到后台约 5 秒内就会被挂起。本插件 iOS 端提供:
- 屏幕常亮(
isIdleTimerDisabled):App 在前台时防止息屏/锁屏。 - 短时后台任务(
beginBackgroundTask):退后台后争取约 30 秒 ~ 3 分钟的收尾时间。
需要长期后台运行怎么办
必须在 manifest.json 的 ios.UIBackgroundModes 或插件 Info.plist 中声明对应模式,并真实使用该能力:
| 后台模式 | 适用场景 | 保活效果 |
|---|---|---|
audio |
音频播放类(配合静默音频循环播放) | 可持续后台运行 |
location |
定位/轨迹类(持续定位) | 可持续后台运行 |
voip |
网络电话 | 可持续后台运行 |
⚠️ 审核风险:苹果会严格审查后台模式的使用,滥用(如无实际音频却声明 audio)会导致 App Store 上架被拒。请按真实业务选择。
当前默认配置了 audio,如不需要可删除 utssdk/app-ios/Info.plist 中的 UIBackgroundModes 配置,或改为 location。
六、使用步骤
1. 引入插件
在页面中导入(已内置在 pages/index/index.vue 的测试示例中):
import {
checkPermissions,
requestPermissions,
startKeepAlive,
stopKeepAlive,
isKeepAliveRunning,
requestIgnoreBatteryOptimization,
isIgnoringBatteryOptimization
} from '@/uni_modules/applive-keepalive';
2. 完整调用流程(推荐)
<template>
<view class="content">
<view class="header">
<text class="title">后台保活测试</text>
<text class="subtitle">{{platformName}} UTS 插件</text>
</view>
<!-- 保活运行状态 -->
<view class="card">
<text class="card-title">保活状态</text>
<view class="info-row">
<text class="info-label">服务运行中</text>
<text class="info-value" :class="running ? 'on' : 'off'">{{ running ? '是' : '否' }}</text>
</view>
<view class="info-row">
<text class="info-label">已运行时长</text>
<text class="info-value">{{ uptimeText }}</text>
</view>
<view class="info-row">
<text class="info-label">服务启动时间</text>
<text class="info-value">{{ serviceStartTime }}</text>
</view>
</view>
<!-- 权限清单 -->
<view class="card">
<text class="card-title">权限清单</text>
<view v-for="(p, index) in permissions" :key="index" class="info-row">
<text class="info-label">{{ p.label }}</text>
<text class="info-value" :class="p.ok ? 'on' : 'off'">{{ p.value }}</text>
</view>
<text class="tip">说明:前台服务 / WakeLock 为清单权限,安装即授予;通知权限需运行时申请;电池优化白名单需跳转系统设置手动开启。</text>
</view>
<!-- 验证说明 -->
<view class="card">
<text class="card-title">如何验证保活是否生效</text>
<text class="tip">1. 点击「启动保活」,「运行时长」开始计时</text>
<text class="tip">2. 点击「退到后台测试」(Android)或按 Home 键(iOS)</text>
<text class="tip">3. 等待 30 秒 ~ 2 分钟</text>
<text class="tip">4. 重新打开 App,观察状态:</text>
<text class="tip ok">✓ 运行时长持续增长、启动时间未变 → 保活生效(进程一直存活)</text>
<text class="tip warn">✓ 启动时间变了(时长归零重计)→ 服务被杀后自动重启</text>
<text class="tip bad">✗ 显示「否」→ 保活失败(检查权限 / 系统限制)</text>
</view>
<!-- 操作按钮 -->
<view class="btn-group">
<button class="btn" type="primary" @click="onStartKeepAlive">启动保活</button>
<button class="btn" type="warn" @click="onStopKeepAlive">停止保活</button>
<button class="btn" type="default" @click="onRequestPermissions">申请权限</button>
<button class="btn" type="default" @click="onCheckPermissions">检测权限</button>
<button v-if="!isIos" class="btn" type="default" @click="onRequestIgnoreBattery">申请电池优化白名单</button>
<button v-if="!isIos" class="btn" type="default" @click="onGoBackground">退到后台测试</button>
</view>
<!-- 操作日志 -->
<view class="card">
<text class="card-title">操作日志</text>
<scroll-view class="log-box" scroll-y>
<text v-for="(log, index) in logs" :key="index" class="log-item">{{ log }}</text>
</scroll-view>
<button class="btn-clear" size="mini" @click="logs = []">清空日志</button>
</view>
</view>
</template>
<script>
import {
checkPermissions,
requestPermissions,
startKeepAlive,
stopKeepAlive,
getKeepAliveInfo,
goBackground,
requestIgnoreBatteryOptimization,
isIgnoringBatteryOptimization
} from '@/uni_modules/applive-keepalive';
export default {
data() {
return {
running: false,
uptimeText: '0 秒',
serviceStartTime: '-',
permissions: [],
logs: [],
pollTimer: null,
platformName: '',
isIos: false,
};
},
onLoad() {
this.addLog('页面加载完成');
this.onCheckPermissions();
this.resolvePlatform();
// 每秒轮询一次,实时刷新「运行时长」,验证保活是否持续生效
// #ifdef APP-PLUS
this.pollTimer = setInterval(() => {
this.refreshKeepAliveInfo();
}, 1000);
// #endif
},
onShow() {
// 从后台回到前台时立即刷新一次
// #ifdef APP-PLUS
this.refreshKeepAliveInfo();
// #endif
},
onUnload() {
if (this.pollTimer) {
clearInterval(this.pollTimer);
this.pollTimer = null;
}
},
methods: {
// 识别当前平台(用于页面提示)
resolvePlatform() {
// #ifdef APP-PLUS
const systemInfo = uni.getSystemInfoSync();
if (systemInfo.platform == 'ios') {
this.platformName = 'App (iOS)'
this.isIos = true
} else if (systemInfo.platform === 'android') {
this.platformName = 'App (Android)'
} else {
this.platformName = 'App (其他平台)'
}
// #endif
// #ifdef MP-WEIXIN
this.platformName = '微信小程序'
// #endif
// #ifdef H5
this.platformName = 'H5(插件返回 notSupported)'
// #endif
},
addLog(msg) {
const time = new Date().toLocaleTimeString();
this.logs.push('[' + time + '] ' + msg);
if (this.logs.length > 200) {
this.logs.shift();
}
},
// 读取服务运行信息,刷新页面状态
refreshKeepAliveInfo() {
// #ifdef APP-PLUS
const info = getKeepAliveInfo();
this.running = info.running;
if (info.running) {
this.uptimeText = this.formatDuration(info.uptime);
this.serviceStartTime = this.formatTime(info.startTime);
} else {
this.uptimeText = '0 秒';
this.serviceStartTime = '-';
}
// #endif
},
formatDuration(seconds) {
const s = Math.floor(seconds);
if (s < 60) {
return s + ' 秒';
}
const m = Math.floor(s / 60);
const remain = s % 60;
return m + ' 分 ' + remain + ' 秒';
},
formatTime(timestamp) {
if (!timestamp) {
return '-';
}
const d = new Date(timestamp);
const pad = (n) => (n < 10 ? '0' + n : '' + n);
return pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
},
formatPermission(status) {
if (status === 'granted') {
return '已授予';
}
if (status === 'denied') {
return '未授予';
}
if (status === 'notDetermined') {
return '未询问';
}
return status || '-';
},
// 用检测结果刷新权限清单
buildPermissionList(res) {
const list = [];
list.push({
label: '前台服务权限',
value: res.foregroundService ? '已授予' : '不支持',
ok: !!res.foregroundService
});
list.push({
label: '保持CPU唤醒 (WakeLock)',
value: res.wakeLock ? '已授予' : '不支持',
ok: !!res.wakeLock
});
list.push({
label: '通知权限',
value: this.formatPermission(res.notification),
ok: res.notification === 'granted'
});
if (!this.isIos) {
list.push({
label: '电池优化白名单',
value: res.ignoreBatteryOptimization ? '已加入' : '未加入',
ok: !!res.ignoreBatteryOptimization
});
}
this.permissions = list;
},
onCheckPermissions() {
// #ifdef APP-PLUS
checkPermissions((res) => {
this.buildPermissionList(res);
this.addLog('权限检测完成');
});
// #endif
// #ifndef APP-PLUS
this.addLog('当前非 App 平台,UTS 插件需在真机运行');
// #endif
},
onRequestPermissions() {
// #ifdef APP-PLUS
requestPermissions((res) => {
this.addLog('申请权限:' + res.message);
// 申请后重新检测刷新清单
this.onCheckPermissions();
});
// #endif
// #ifndef APP-PLUS
this.addLog('当前非 App 平台,无法申请权限');
// #endif
},
onRequestIgnoreBattery() {
if (!this.isIos) {
const res = requestIgnoreBatteryOptimization();
this.addLog(res.message);
setTimeout(() => {
const added = isIgnoringBatteryOptimization();
this.addLog('电池优化白名单:' + (added ? '已加入' : '未加入'));
this.onCheckPermissions();
}, 1000);
}
},
onStartKeepAlive() {
// #ifdef APP-PLUS
const res = startKeepAlive({
title: '应用保活中',
content: '应用正在后台运行,请勿关闭'
});
this.addLog(res.message);
this.refreshKeepAliveInfo();
// #endif
// #ifndef APP-PLUS
this.addLog('当前非 App 平台,无法启动保活');
// #endif
},
onStopKeepAlive() {
// #ifdef APP-PLUS
const res = stopKeepAlive();
this.addLog(res.message);
this.refreshKeepAliveInfo();
// #endif
// #ifndef APP-PLUS
this.addLog('当前非 App 平台,无法停止保活');
// #endif
},
onGoBackground() {
if (!this.isIos) {
const res = goBackground();
this.addLog(res.message);
}
}
}
};
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
padding: 30rpx;
box-sizing: border-box;
}
.header {
display: flex;
flex-direction: column;
align-items: center;
margin: 20rpx 0 30rpx;
}
.title {
font-size: 40rpx;
font-weight: bold;
color: #333333;
}
.subtitle {
font-size: 24rpx;
color: #999999;
margin-top: 10rpx;
}
.card {
width: 100%;
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
box-sizing: border-box;
}
.card-title {
font-size: 30rpx;
font-weight: bold;
color: #333333;
display: block;
margin-bottom: 20rpx;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.info-label {
font-size: 28rpx;
color: #666666;
}
.info-value {
font-size: 28rpx;
color: #333333;
font-weight: bold;
}
.info-value.on {
color: #07c160;
}
.info-value.off {
color: #fa5151;
}
.tip {
display: block;
font-size: 24rpx;
color: #999999;
line-height: 1.8;
margin-top: 10rpx;
}
.tip.ok {
color: #07c160;
}
.tip.warn {
color: #ff9900;
}
.tip.bad {
color: #fa5151;
}
.btn-group {
width: 100%;
display: flex;
flex-direction: column;
margin-bottom: 24rpx;
}
.btn {
margin-bottom: 20rpx;
}
.log-box {
height: 360rpx;
background-color: #f7f7f7;
border-radius: 8rpx;
padding: 16rpx;
box-sizing: border-box;
}
.log-item {
display: block;
font-size: 24rpx;
color: #555555;
line-height: 1.8;
word-break: break-all;
}
.btn-clear {
margin-top: 20rpx;
}
</style>
3. 打包运行
- 使用 HBuilderX 打开项目(UTS 插件不支持 CLI 方式)。
- UTS 插件需自定义基座后才能生效,在 HBuilderX 中:运行 → 运行到手机或模拟器 → 制作自定义调试基座。
- 真机运行或云打包测试。
七、常见问题(FAQ)
Q1:为什么我调用后没有反应? A:UTS 插件必须使用「自定义基座」运行,标准基座不含插件代码。请在 HBuilderX 中制作自定义调试基座。
Q2:Android 保活后通知栏能去掉常驻通知吗? A:不能。Android 8.0+ 前台服务必须显示常驻通知,这是系统强制要求。通知渠道已设置为低优先级(静默)。
Q3:iOS 能像 Android 一样一直后台运行吗? A:不能。iOS 系统架构决定了普通应用无法无限期后台运行,只能依赖合法的后台模式(音频/定位/VoIP)。
Q4:国产手机保活效果差怎么办? A:国产 ROM 有额外后台限制,需引导用户手动开启自启动、关闭省电限制,无法完全通过代码解决。
Q5:Service 类名 / 包名规则是什么?
A:uni_modules 下 UTS 插件默认包名为 uts.sdk.modules.<插件ID转驼峰>。本插件 ID 为 applive-keepalive,转驼峰为 appliveKeepalive,因此 Service 全名为 uts.sdk.modules.appliveKeepalive.KeepAliveService。如修改插件 ID,需同步修改 AndroidManifest.xml 中的 android:name。
八、兼容性
- Android:minSdkVersion 23(Android 6.0),完整前台服务保活需 Android 8.0+。
- iOS:deploymentTarget 11.0。
- HBuilderX:3.8.2+(
requestSystemPermission需此版本以上)。

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 1
赞赏 0
下载 12739099
赞赏 1959
赞赏
京公网安备:11010802035340号