「已注销」 2025-12-03 12:04 采纳率: 25%
浏览 3

应用退出后服务唤醒问题

#主要问题表现:我在设置闹钟后,应用在不退出的情况下正常唤醒,应用在退出时无法唤醒.麻烦各位帮忙看下
注册代码:

        <!-- 服务声明 -->
        <service
            android:name=".service.AlarmJobService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="true" />

        <!-- 广播接收器 -->
        <receiver android:name=".work.AlarmReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"  />
            </intent-filter>
        </receiver>

    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
    <uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

    <!-- 针对中国厂商的特殊声明 -->
    <uses-permission android:name="com.xiaomi.permission.SCHEDULE_EXACT_ALARM"/>
    <uses-permission android:name="com.huawei.permission.SCHEDULE_EXACT_ALARM"/>

调用代码:


class AlarmClockActivity : BaseActivity() {

    override fun getLayoutResId(): Int {
        return R.layout.act_alarm_clock
    }

    override fun initActivity() {
        checkExactAlarmPermission()
        scheduleAlarm()
    }

    // TODO: 2025/12/2 16:03 -(Administrator)-{🎉  内部调用}

    private fun checkExactAlarmPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager
            if (!alarmManager.canScheduleExactAlarms())  {
                // 请求SCHEDULE_EXACT_ALARM权限
                startActivity(Intent(ACTION_REQUEST_SCHEDULE_EXACT_ALARM))
            }
        }
    }

    private fun scheduleAlarm() {
        val calendar: Calendar = Calendar.getInstance().apply  {
            timeInMillis = System.currentTimeMillis()
            set(Calendar.HOUR_OF_DAY, 12)
            set(Calendar.MINUTE, 0)
        }

        // 确保时间在未来
        var triggerTime = calendar.timeInMillis
        if (triggerTime <= System.currentTimeMillis())  {
            triggerTime += 24 * 60 * 60 * 1000 // 推迟到第二天
        }

        // 方案1: 使用JobScheduler
        scheduleWithJobScheduler(triggerTime)

        // 方案2: 使用AlarmManager作为备用
        scheduleWithAlarmManager(triggerTime)
    }

    private fun scheduleWithJobScheduler(triggerTime: Long) {
        try {
            val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
            val componentName = ComponentName(this, AlarmJobService::class.java)

            val delay = triggerTime - System.currentTimeMillis()
            val jobInfo = JobInfo.Builder(0, componentName)
                .setMinimumLatency(delay)
                .setOverrideDeadline(delay + 1000)
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
                .setPersisted(true)
                .build()

            val result = jobScheduler.schedule(jobInfo)
            if (result == JobScheduler.RESULT_SUCCESS) {
                Log.d("AlarmClockActivity", "Job scheduled successfully")
            } else {
                Log.e("AlarmClockActivity", "Job scheduling failed")
                // 失败时回退到AlarmManager
                scheduleWithAlarmManager(triggerTime)
            }
        } catch (e: Exception) {
            Log.e("AlarmClockActivity", "JobScheduler error", e)
            scheduleWithAlarmManager(triggerTime)
        }
    }

    private fun scheduleWithAlarmManager(triggerTime: Long) {
        val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager
        val intent = Intent(this, AlarmReceiver::class.java)
        val pendingIntent = PendingIntent.getBroadcast(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setExactAndAllowWhileIdle(
                AlarmManager.RTC_WAKEUP,
                triggerTime,
                pendingIntent
            )
        } else {
            alarmManager.setExact(
                AlarmManager.RTC_WAKEUP,
                triggerTime,
                pendingIntent
            )
        }
        Log.d("AlarmClockActivity", "Alarm scheduled with AlarmManager")
    }

    // TODO: 2025/12/2 16:13 -(Administrator)-{🎉  全局调用}

    companion object {
        fun startActivity(context: Context) {
            val intent = Intent(context, AlarmClockActivity::class.java)
            context.startActivity(intent)
        }
    }
}

服务代码:


class AlarmJobService : JobService(){

    override fun onStartJob(params: JobParameters?): Boolean {
        showNotification()
        return false
    }

    override fun onStopJob(params: JobParameters?): Boolean {
        return false
    }

    private fun showNotification() {
        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                "alarm_channel",
                "Alarm Notifications",
                NotificationManager.IMPORTANCE_HIGH
            )
            notificationManager.createNotificationChannel(channel)
        }

        val notification = NotificationCompat.Builder(this, "alarm_channel")
            .setContentTitle("闹钟")
            .setContentText("时间到了!")
            .setSmallIcon(R.mipmap.app_icon_naozhou)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .build()

        notificationManager.notify(1,  notification)
    }
}

接受器代码:


class AlarmReceiver : BroadcastReceiver(){
    @SuppressLint("ScheduleExactAlarm")
    override fun onReceive(context: Context, intent: Intent?) {
        when (intent?.action) {
            Intent.ACTION_BOOT_COMPLETED -> {
                // 设备重启后重新设置闹钟
                val prefs = context.getSharedPreferences("alarm_prefs",  Context.MODE_PRIVATE)
                val triggerTime = prefs.getLong("next_alarm_time",  0)
                if (triggerTime > 0) {
                    val alarmManager = context.getSystemService(ALARM_SERVICE)  as AlarmManager
                    val alarmIntent = Intent(context, AlarmReceiver::class.java)
                    val pendingIntent = PendingIntent.getBroadcast(
                        context,
                        0,
                        alarmIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                    )

                    try {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            alarmManager.setExactAndAllowWhileIdle(
                                AlarmManager.RTC_WAKEUP,
                                triggerTime,
                                pendingIntent
                            )
                        } else {
                            alarmManager.setExact(
                                AlarmManager.RTC_WAKEUP,
                                triggerTime,
                                pendingIntent
                            )
                        }
                    }catch (e : Exception){
                        e.printStackTrace()
                    }
                }
            }
            else -> {
                // 处理闹钟触发
                val serviceIntent = Intent(context, AlarmJobService::class.java)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    context.startForegroundService(serviceIntent)
                } else {
                    context.startService(serviceIntent)
                }
            }
        }
    }
}
  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2025-12-03 12:37
    关注

    中午好🌞🌞🌞
    本答案参考通义千问

    你遇到的问题是应用退出后无法唤醒服务,这在 Android 系统中是一个常见的问题。尤其是在 Android 8.0(API 26)及以上版本,系统对后台服务和广播的限制更加严格。


    ✅ 问题分析

    1. 应用退出后,进程被终止

    当应用完全退出时,系统会终止其进程,因此任何通过 JobSchedulerAlarmManager 触发的逻辑可能无法正常执行。

    2. BroadcastReceiver 的 exported="false" 设置

    你的 AlarmReceiver 设置为 android:exported="false",意味着它只能由同一个应用的组件调用。但如果你使用的是 AlarmManager 来触发这个 Receiver,而没有正确的权限或方式,可能会导致无法触发。

    3. JobScheduler 的限制

    • JobScheduler 在应用退出后无法运行。
    • 如果应用被系统杀死,Job 也会被取消。

    4. AlarmManager 的行为

    • setExactAndAllowWhileIdle() 是一种更精确的定时器,但在某些设备上可能受到限制。
    • 如果未正确设置 PendingIntent,也可能导致无法唤醒。

    🔧 解决方案

    以下是你需要进行的修改和优化:

    解决方案一:使用前台服务 + JobScheduler

    重点:前台服务可以保持应用在后台运行,避免被系统杀死。

    修改步骤如下:

    1. 创建一个前台服务来监听闹钟事件
      • 使用 startForegroundService() 启动服务
      • 在服务中使用 JobSchedulerAlarmManager 来调度任务
    class AlarmJobService : JobService() {
    
        override fun onStartJob(params: JobParameters): Boolean {
            // 执行你的业务逻辑
            Log.d("AlarmJobService", "Job started")
            return true
        }
    
        override fun onStopJob(params: JobParameters): Boolean {
            return false
        }
    }
    
    1. 在 Activity 中启动前台服务
    private fun scheduleWithJobScheduler(triggerTime: Long) {
        try {
            val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
            val componentName = ComponentName(this, AlarmJobService::class.java)
    
            val delay = triggerTime - System.currentTimeMillis()
            val jobInfo = JobInfo.Builder(0, componentName)
                .setMinimumLatency(delay)
                .setOverrideDeadline(delay + 1000)
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
                .setPersisted(true)
                .build()
    
            val result = jobScheduler.schedule(jobInfo)
            if (result == JobScheduler.RESULT_SUCCESS) {
                Log.d("AlarmClockActivity", "Job scheduled successfully")
            } else {
                Log.e("AlarmClockActivity", "Job scheduling failed")
                scheduleWithAlarmManager(triggerTime)
            }
        } catch (e: Exception) {
            Log.e("AlarmClockActivity", "JobScheduler error", e)
            scheduleWithAlarmManager(triggerTime)
        }
    }
    
    1. scheduleAlarm() 方法中加入启动前台服务的逻辑
    private fun scheduleAlarm() {
        // ... 上面的代码不变 ...
    
        // 启动前台服务
        startForegroundService(Intent(this, AlarmJobService::class.java))
    }
    

    解决方案二:使用 AlarmManagerBroadcastReceiver 并确保其可被唤醒

    重点:确保 BroadcastReceiver 被正确注册,并且具备足够的权限。

    修改步骤如下:

    1. 确保 AlarmReceiver 可以被外部调用(不推荐 exported="false"
    <receiver android:name=".work.AlarmReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="com.yourapp.ALARM_ACTION" />
        </intent-filter>
    </receiver>
    

    注意:将 com.yourapp.ALARM_ACTION 替换为实际的 Action 名称。

    1. scheduleWithAlarmManager() 中使用自定义 Action
    private fun scheduleWithAlarmManager(triggerTime: Long) {
        val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager
        val intent = Intent("com.yourapp.ALARM_ACTION") // 自定义 Action
        val pendingIntent = PendingIntent.getBroadcast(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setExactAndAllowWhileIdle(
                AlarmManager.RTC_WAKEUP,
                triggerTime,
                pendingIntent
            )
        } else {
            alarmManager.setExact(
                AlarmManager.RTC_WAKEUP,
                triggerTime,
                pendingIntent
            )
        }
    }
    
    1. AlarmReceiver 中处理广播并启动服务
    class AlarmReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent?) {
            // 启动前台服务
            val serviceIntent = Intent(context, AlarmJobService::class.java)
            context.startService(serviceIntent)
        }
    }
    

    解决方案三:使用 WorkManager(推荐)

    重点:WorkManager 是 Android 推荐的后台任务调度方式,兼容性强,适用于大多数场景。

    修改步骤如下:

    1. 添加依赖(如果还没有)
    implementation 'androidx.work:work-runtime-ktx:2.7.1'
    
    1. 创建 Worker 类
    class AlarmWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
        override suspend fun doWork(): Result {
            // 执行你的业务逻辑
            Log.d("AlarmWorker", "Alarm triggered")
            return Result.success()
        }
    }
    
    1. scheduleAlarm() 中使用 WorkManager
    private fun scheduleWithWorkManager(triggerTime: Long) {
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.NONE)
            .build()
    
        val workRequest = OneTimeWorkRequestBuilder<AlarmWorker>()
            .setInitialDelay(triggerTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
            .setConstraints(constraints)
            .build()
    
        WorkManager.getInstance(this).enqueue(workRequest)
    }
    
    1. 修改 scheduleAlarm() 方法
    private fun scheduleAlarm() {
        // ... 原有代码 ...
    
        // 使用 WorkManager
        scheduleWithWorkManager(triggerTime)
    }
    

    📌 总结

    | 方案 | 优点 | 缺点 | |------|------|------| | JobScheduler + 前台服务 | 兼容性较好 | 需要手动管理服务生命周期 | | AlarmManager + BroadcastReceiver | 精确控制时间 | 受限于系统策略,稳定性差 | | WorkManager | 最推荐,稳定、兼容性强 | 需要引入额外依赖 |


    ✅ 最终建议

    • 优先使用 WorkManager,这是目前 Android 推荐的方式,适合大多数后台任务。
    • 如果必须使用 AlarmManager,请确保 BroadcastReceiverexported="true",并使用自定义 Action。
    • 如果你希望在应用退出后仍然能唤醒服务,必须使用前台服务

    如有更多关于后台任务或 Android 13 以上系统的适配问题,欢迎继续提问!

    评论

报告相同问题?

问题事件

  • 创建了问题 12月3日