通过AlarmManager添加定时任务,即使应用被杀死了,也会启动intent
用户静默安装前,定时自启动自身应用。直到应用启动完成,取消其他的定时任务
object InstallAppUtils {
const val DEBUG_UPDATE = false
private const val PACKAGE_NAME = "com.demo.test"
suspend fun installApkCmd(
context: Context,
filepath: String,
onFailed: () -> Unit,
onSuccess: () -> Unit
) {
LogUtils.d("InstallAppUtils installApk filepath=${filepath}")
try {
val apkFile = File(filepath)
scheduleAppRestart(context)
MmkvUtils.saveUpdatingApkFilePath(apkFile.absolutePath)
delay(500)//延迟执行命令,先做好升级前的准备
val process = Runtime.getRuntime()
.exec(arrayOf("su", "0", "pm", "install", "-r", apkFile.absolutePath))
process.waitFor()
if (process.exitValue() == 0) {
// 安装成功
LogUtils.d("APK 静默安装成功")
onSuccess.invoke()
} else {
// 安装失败,可读取错误流以获取详细信息
val errorStream = process.errorStream.bufferedReader().readText()
LogUtils.d("APK 静默安装失败:$errorStream")
apkFile.delete()
onFailed.invoke()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
fun clearUpdatingFile() {
try {
MmkvUtils.getUpdatingApkFilePath()?.let { path ->
if (path.isNotEmpty()) {
LogUtils.d("InstallAppUtils clearUpdatingFile $path")
val file = File(path)
if (file.exists()) {
file.delete()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
MmkvUtils.clearUpdatingApkFilePath()
}
}
/**
* 通过AlarmManager尝试每隔10秒启动应用
*/
fun scheduleAppRestart(context: Context) {
// 构造启动应用的 Intent,这里假设 MainActivity 为入口
var triggerAtMillis = System.currentTimeMillis() + 10_000L
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
repeat(6) {
triggerAtMillis += 10_000L
getRestartPendingIntent(context, it)?.let { pendingIntent ->
// setExactAndAllowWhileIdle 确保在低功耗模式下也能执行
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
triggerAtMillis,
pendingIntent
)
}
}
}
// 取消启动应用的定时任务
fun cancelAlarm(context: Context) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
repeat(6) {
getRestartPendingIntent(context, it)?.let { pendingIntent ->
alarmManager.cancel(pendingIntent)
}
}
}
private fun getRestartPendingIntent(context: Context, requestCode: Int): PendingIntent? {
// val intent = Intent().apply {
// component = ComponentName(PACKAGE_NAME, LAUNCH_ACTIVITY)
// addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
// }
val launchIntent: Intent? =
getPackageManager().getLaunchIntentForPackage(PACKAGE_NAME)
return PendingIntent.getActivity(
context,
requestCode,
launchIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
}
}