feat(boot): 新增前台服务中转实现开机自启动
使用 BootService 前台服务绕过 Android 10+ 后台启动 Activity 限制, 通过 setFullScreenIntent 触发 InitActivity,兼容主从设备差异。 同时优化 MasterScaleActivity、SettingActivity 及相关布局。
This commit is contained in:
@@ -3,15 +3,25 @@ package com.shuwei.dish.match.utils
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.shuwei.dish.match.ui.InitActivity
|
||||
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
|
||||
companion object {
|
||||
/** 兼容多种设备的开机广播 action 集合 */
|
||||
private val BOOT_ACTIONS = setOf(
|
||||
Intent.ACTION_BOOT_COMPLETED,
|
||||
"android.intent.action.QUICKBOOT_POWERON",
|
||||
"android.intent.action.LOCKED_BOOT_COMPLETED"
|
||||
)
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
|
||||
// 启动服务或Activity
|
||||
val launchIntent = Intent(context, InitActivity::class.java)
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(launchIntent)
|
||||
val action = intent.action ?: return
|
||||
if (action in BOOT_ACTIONS) {
|
||||
// Android 10+ 禁止在后台直接启动 Activity
|
||||
// 改为启动前台服务,由 BootService 负责拉起 InitActivity
|
||||
val serviceIntent = Intent(context, BootService::class.java)
|
||||
context.startForegroundService(serviceIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.shuwei.dish.match.utils
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.ui.InitActivity
|
||||
|
||||
/**
|
||||
* 开机自启中转前台服务
|
||||
* 用于绕过 Android 10+ 对后台直接启动 Activity 的限制
|
||||
* 执行流程:BootReceiver → startForegroundService() → 本服务 → setFullScreenIntent → InitActivity
|
||||
*/
|
||||
class BootService : Service() {
|
||||
|
||||
companion object {
|
||||
/** 通知渠道 ID */
|
||||
private const val CHANNEL_ID = "boot_channel"
|
||||
/** 前台通知 ID */
|
||||
private const val NOTIFICATION_ID = 1001
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
// 若 App 进程已在前台运行(主设备系统签名场景),则不重复启动,避免闪退
|
||||
val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
|
||||
val isAppForeground = activityManager.runningAppProcesses?.any {
|
||||
it.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
||||
&& it.processName == packageName
|
||||
} ?: false
|
||||
|
||||
// App 未运行时才设置 fullScreenIntent,由系统自动启动 Activity
|
||||
// 直接调用 startActivity() 在部分 Android 10 设备上会被 ActivityTaskManager 拦截
|
||||
val pendingIntent = if (!isAppForeground) createLaunchPendingIntent() else null
|
||||
|
||||
// Android 14+ startForeground 需要传入 foregroundServiceType
|
||||
val notification = buildNotification(pendingIntent)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
/** 创建启动 InitActivity 的 PendingIntent */
|
||||
private fun createLaunchPendingIntent(): PendingIntent {
|
||||
val launchIntent = Intent(this, InitActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
return PendingIntent.getActivity(
|
||||
this, 0, launchIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建前台服务通知
|
||||
* fullScreenIntent 不为空时设置全屏意图,系统会在显示通知时自动启动 Activity
|
||||
* 通知渠道必须为 IMPORTANCE_HIGH,否则 fullScreenIntent 不会触发
|
||||
*/
|
||||
private fun buildNotification(fullScreenIntent: PendingIntent?): Notification {
|
||||
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"开机自启动",
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
).apply {
|
||||
description = "应用开机自动启动通知渠道"
|
||||
setShowBadge(false)
|
||||
}
|
||||
manager.createNotificationChannel(channel)
|
||||
|
||||
return Notification.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("正在启动应用")
|
||||
.setSmallIcon(R.mipmap.ic_logo512)
|
||||
.apply {
|
||||
if (fullScreenIntent != null) {
|
||||
setFullScreenIntent(fullScreenIntent, true)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user