// index.uts - Android 应用保活插件
|
|
// 引用 Android API
|
import Context from "android.content.Context";
|
import Intent from "android.content.Intent";
|
import Build from "android.os.Build";
|
import NotificationChannel from "android.app.NotificationChannel";
|
import NotificationManager from "android.app.NotificationManager";
|
import Notification from "android.app.Notification";
|
import PendingIntent from "android.app.PendingIntent";
|
import PowerManager from "android.os.PowerManager";
|
import BatteryManager from "android.os.BatteryManager";
|
import { UTSAndroid } from "io.dcloud.uts";
|
|
// 保活服务的通知渠道ID
|
const CHANNEL_ID = "keep_alive_channel";
|
const NOTIFICATION_ID: Int = 1001;
|
|
// 保活服务是否已启动的标志
|
let isKeepAliveRunning = false;
|
|
// WakeLock 引用,用于保持 CPU 运行
|
let wakeLock: PowerManager.WakeLock | null = null;
|
|
/**
|
* 启动应用保活
|
* 通过 WakeLock + 通知来保持应用在后台运行
|
*/
|
export function keepAliveStart(): void {
|
const context = UTSAndroid.getAppContext();
|
if (context == null) {
|
console.error("keepAliveStart: 获取应用上下文失败");
|
return;
|
}
|
|
if (isKeepAliveRunning) {
|
console.log("keepAliveStart: 保活服务已在运行中");
|
return;
|
}
|
|
try {
|
// 1. 获取 WakeLock 唤醒锁,保持 CPU 运行
|
acquireWakeLock(context);
|
|
// 2. 创建通知渠道 (Android 8.0+)
|
createNotificationChannel(context);
|
|
// 3. 创建并显示前台通知
|
showForegroundNotification(context);
|
|
// 4. 注册应用生命周期监听
|
registerLifecycleCallbacks();
|
|
isKeepAliveRunning = true;
|
console.log("keepAliveStart: 保活服务启动成功(WakeLock已获取)");
|
} catch (e) {
|
console.error("keepAliveStart: 启动失败", e);
|
}
|
}
|
|
/**
|
* 停止应用保活
|
*/
|
export function keepAliveStop(): void {
|
const context = UTSAndroid.getAppContext();
|
if (context == null) {
|
return;
|
}
|
|
try {
|
// 释放 WakeLock
|
releaseWakeLock();
|
|
// 取消通知
|
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
|
notificationManager.cancel(NOTIFICATION_ID);
|
|
isKeepAliveRunning = false;
|
console.log("keepAliveStop: 保活服务已停止(WakeLock已释放)");
|
} catch (e) {
|
console.error("keepAliveStop: 停止失败", e);
|
}
|
}
|
|
/**
|
* 检查保活服务是否正在运行
|
*/
|
export function isKeepAliveActive(): boolean {
|
return isKeepAliveRunning;
|
}
|
|
/**
|
* 获取电池电量百分比
|
*/
|
export function getBatteryCapacity(): string {
|
const context = UTSAndroid.getAppContext();
|
if (context != null) {
|
const manager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager;
|
const currentLevel: number = manager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
|
return '' + currentLevel + '%';
|
}
|
return "0%";
|
}
|
|
/**
|
* 请求忽略电池优化(需要用户手动确认)
|
* 调用此方法会弹出系统设置页面
|
*/
|
export function requestIgnoreBatteryOptimization(): void {
|
const context = UTSAndroid.getAppContext();
|
if (context == null) {
|
return;
|
}
|
|
if (Build.VERSION.SDK_INT >= 23) { // Android 6.0+
|
const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager;
|
const packageName = context.getPackageName();
|
|
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
|
try {
|
const intent = new Intent();
|
intent.setAction("android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS");
|
intent.setData(android.net.Uri.parse("package:" + packageName));
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
context.startActivity(intent);
|
} catch (e) {
|
console.error("requestIgnoreBatteryOptimization: 打开设置失败", e);
|
}
|
} else {
|
console.log("requestIgnoreBatteryOptimization: 已在白名单中");
|
}
|
}
|
}
|
|
/**
|
* 检查是否已忽略电池优化
|
*/
|
export function isIgnoringBatteryOptimizations(): boolean {
|
const context = UTSAndroid.getAppContext();
|
if (context == null) {
|
return false;
|
}
|
|
if (Build.VERSION.SDK_INT >= 23) {
|
const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager;
|
const packageName = context.getPackageName();
|
return powerManager.isIgnoringBatteryOptimizations(packageName);
|
}
|
|
return true; // Android 6.0 以下默认返回 true
|
}
|
|
/**
|
* 创建通知渠道 (Android 8.0+ 必需)
|
*/
|
function createNotificationChannel(context: Context): void {
|
if (Build.VERSION.SDK_INT >= 26) { // Android 8.0+
|
const channel = new NotificationChannel(
|
CHANNEL_ID,
|
"应用保活服务",
|
NotificationManager.IMPORTANCE_LOW
|
);
|
channel.setDescription("保持应用在后台运行");
|
channel.setShowBadge(false);
|
channel.enableLights(false);
|
channel.enableVibration(false);
|
|
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
|
notificationManager.createNotificationChannel(channel);
|
}
|
}
|
|
/**
|
* 显示前台通知
|
*/
|
function showForegroundNotification(context: Context): void {
|
// 获取应用的启动 Intent
|
const packageManager = context.getPackageManager();
|
const launchIntent = packageManager.getLaunchIntentForPackage(context.getPackageName());
|
|
let pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT;
|
if (Build.VERSION.SDK_INT >= 31) { // Android 12+
|
pendingIntentFlags = pendingIntentFlags | PendingIntent.FLAG_IMMUTABLE;
|
}
|
|
const pendingIntent = PendingIntent.getActivity(
|
context,
|
0,
|
launchIntent,
|
pendingIntentFlags
|
);
|
|
// 构建通知
|
let builder: Notification.Builder;
|
if (Build.VERSION.SDK_INT >= 26) {
|
builder = new Notification.Builder(context, CHANNEL_ID);
|
} else {
|
builder = new Notification.Builder(context);
|
}
|
|
// 获取应用图标
|
const appInfo = context.getApplicationInfo();
|
const appIcon = appInfo.icon;
|
const appName = packageManager.getApplicationLabel(appInfo).toString();
|
|
builder.setSmallIcon(appIcon)
|
.setContentTitle(appName)
|
.setContentText("服务运行中")
|
.setContentIntent(pendingIntent)
|
.setOngoing(true) // 常驻通知,不可被滑动删除
|
.setPriority(Notification.PRIORITY_LOW);
|
|
if (Build.VERSION.SDK_INT >= 21) {
|
builder.setCategory(Notification.CATEGORY_SERVICE);
|
}
|
|
const notification = builder.build();
|
|
// 显示通知
|
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
|
notificationManager.notify(NOTIFICATION_ID, notification);
|
}
|
|
/**
|
* 获取 WakeLock 唤醒锁
|
* PARTIAL_WAKE_LOCK: 保持 CPU 运行,屏幕和键盘背光可以关闭
|
*/
|
function acquireWakeLock(context: Context): void {
|
if (wakeLock != null && wakeLock!.isHeld()) {
|
console.log("acquireWakeLock: WakeLock 已持有");
|
return;
|
}
|
|
try {
|
const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager;
|
// PARTIAL_WAKE_LOCK 保持 CPU 运行
|
wakeLock = powerManager.newWakeLock(
|
PowerManager.PARTIAL_WAKE_LOCK,
|
"KeepAlive::WakeLock"
|
);
|
|
if (wakeLock != null) {
|
// 设置超时时间为无限(传 0 或负数表示无限)
|
// 使用 acquire() 无参版本
|
wakeLock!.setReferenceCounted(false);
|
wakeLock!.acquire();
|
console.log("acquireWakeLock: WakeLock 获取成功");
|
}
|
} catch (e) {
|
console.error("acquireWakeLock: WakeLock 获取失败", e);
|
}
|
}
|
|
/**
|
* 释放 WakeLock 唤醒锁
|
*/
|
function releaseWakeLock(): void {
|
if (wakeLock != null && wakeLock!.isHeld()) {
|
try {
|
wakeLock!.release();
|
wakeLock = null;
|
console.log("releaseWakeLock: WakeLock 已释放");
|
} catch (e) {
|
console.error("releaseWakeLock: WakeLock 释放失败", e);
|
}
|
}
|
}
|
|
/**
|
* 注册应用生命周期回调
|
* 当应用进入后台时,确保 WakeLock 保持持有
|
*/
|
function registerLifecycleCallbacks(): void {
|
// 应用进入后台时的回调
|
UTSAndroid.onAppActivityPause(() => {
|
console.log("onAppActivityPause: 应用进入后台");
|
// 确保 WakeLock 仍然持有
|
const context = UTSAndroid.getAppContext();
|
if (context != null && isKeepAliveRunning) {
|
if (wakeLock == null || !wakeLock!.isHeld()) {
|
acquireWakeLock(context);
|
}
|
}
|
});
|
|
// 应用返回前台时的回调
|
UTSAndroid.onAppActivityResume(() => {
|
console.log("onAppActivityResume: 应用返回前台");
|
});
|
|
// 应用被销毁时的回调
|
UTSAndroid.onAppActivityDestroy(() => {
|
console.log("onAppActivityDestroy: 应用被销毁");
|
// 释放资源
|
releaseWakeLock();
|
isKeepAliveRunning = false;
|
});
|
|
console.log("registerLifecycleCallbacks: 生命周期回调已注册");
|
}
|