吉安感知网项目-前端
罗广辉
2026-02-27 e00da17ae2adaecf1cdf2d5128e357a50607bc90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// 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'
import AlertDialog from 'android.app.AlertDialog'
import DialogInterface from 'android.content.DialogInterface'
 
// 保活服务的通知渠道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 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
}
 
/**
 * 请求忽略电池优化(需要用户手动确认)
 * 调用此方法会弹出系统设置页面
 */
export function requestIgnoreBatteryOptimization(): void {
  if (isIgnoringBatteryOptimizations()) return
  const activity = UTSAndroid.getUniActivity()
  if (activity == null) return
  AlertDialog.Builder(activity)
    .setTitle('权限提示')
    .setMessage('为了在后台收到来电时能弹出通话界面,请将应用添加到电池优化白名单')
    .setPositiveButton('去设置', (_dialog : DialogInterface, _which : Int) => {
      const context = UTSAndroid.getAppContext()
      if (context == null) return
      if (Build.VERSION.SDK_INT >= 23) {
        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)
          }
        }
      }
    })
    .setNegativeButton('暂不', null)
    .show()
}
 
/**
 * 创建通知渠道 (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: 生命周期回调已注册')
}