更新记录

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 键。


三·补 如何验证保活是否生效

  1. 调用 startKeepAlive() 启动保活,getKeepAliveInfo().startTime 会得到一个时间戳 T0。
  2. 按 Home 键退到后台(Android 可调用 goBackground()),等待 30 秒~2 分钟。
  3. 重新打开 App,调用 getKeepAliveInfo() 观察:
    • running 仍为 true 且 startTime 未变、uptime 持续增长 → 保活生效,进程在后台一直存活;
    • startTime 变了(uptime 归零重计) → 服务曾被系统杀死后自动重启(START_STICKY 生效);
    • running 为 false → 保活失败,检查通知权限 / 电池优化白名单 / 厂商后台限制。

测试页 pages/index/index.vue 已内置以上逻辑(每秒轮询显示运行时长 + 一键退后台按钮)。


四、Android 保活原理

Android 端采用前台服务(Foreground Service)作为核心保活手段,这是 Android 8.0+ 最可靠的合法保活方式:

  1. 前台服务 + 常驻通知:进程优先级提升到「前台」,系统基本不杀。
  2. WakeLock(PARTIAL_WAKE_LOCK):保持 CPU 唤醒,防止息屏后 CPU 休眠。
  3. 电池优化白名单:加入后系统省电策略不会杀掉进程。
  4. 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. 打包运行

  1. 使用 HBuilderX 打开项目(UTS 插件不支持 CLI 方式)。
  2. UTS 插件需自定义基座后才能生效,在 HBuilderX 中:运行 → 运行到手机或模拟器 → 制作自定义调试基座。
  3. 真机运行或云打包测试。

七、常见问题(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 需此版本以上)。

隐私、权限声明

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

"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", "<uses-permission android:name=\"android.permission.VIBRATE\"/>", "<uses-permission android:name=\"android.permission.READ_LOGS\"/>", "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.CAMERA\"/>", "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", "<uses-feature android:name=\"android.hardware.camera\"/>", "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>", "<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\"/>", "<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_DATA_SYNC\"/>", "<uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\"/>", "<uses-permission android:name=\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\"/>"

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

插件不采集任何数据

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

无

暂无用户评论。