Android 14中每2小时触发的PendingIntent未生效排查求助
我有个疑问:每次通过BroadcastReceiver创建OneTimeWorker时,能不能在Worker内部获取当前位置?
我的实现流程:Fragment按钮触发PendingIntent到BroadcastReceiver,Receiver代码如下:
class AlarmManagerReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() val workRequest = OneTimeWorkRequestBuilder<WeatherWorker>() .addTag("OneTimeJobNew") .setConstraints(constraints) .build() WorkManager.getInstance(context!!).enqueue(workRequest) } }
WeatherWorker类代码:
@HiltWorker class WeatherWorker @AssistedInject constructor( @Assisted appContext: Context, @Assisted workerParams: WorkerParameters, private val repository: WeatherRepository, private val fusedLocationClient: FusedLocationProviderClient ) : CoroutineWorker(appContext, workerParams) { override suspend fun doWork(): Result { return try { // 检查后台位置权限 if (ActivityCompat.checkSelfPermission( applicationContext, Manifest.permission.ACCESS_BACKGROUND_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { var nextDayWeather :Any = Any() // 获取位置 val locationResult = fusedLocationClient.lastLocation val taskResult = Tasks.await(locationResult) val lat = taskResult.latitude val long = taskResult.longitude val result = repository.fetchWeather("$lat,$long") if (result.status is Success) { val currentDate = Calendar.getInstance() currentDate.add(Calendar.DAY_OF_YEAR, 1) val nextDate = currentDate.time val dateFormat = SimpleDateFormat("yyyy-MM-dd") var nextDateStr = dateFormat.format(nextDate) val forecastDayResult = result.status.data!!.forecast.forecastday for (dayWeek in forecastDayResult){ if(dayWeek.date.contains(nextDateStr)){ nextDayWeather = dayWeek break } } if(nextDayWeather is forecastday){ AppUtils.notify(applicationContext,"Tomorrow's Weather","Weather Status: " + "${nextDayWeather.day.condition.text} " + " Average Temperature: " + "${nextDayWeather.day.avgtemp_c.toInt()}", R.mipmap.ic_launcher_weather_round) } // 调度2小时后的闹钟 val intent = Intent(applicationContext, AlarmManagerReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast( applicationContext, 1, intent, PendingIntent.FLAG_IMMUTABLE ) val alarmManager = applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager val calendar = Calendar.getInstance() calendar.timeInMillis = System.currentTimeMillis() calendar.add(Calendar.HOUR, 2) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val canScheduleExactAlarms = alarmManager.canScheduleExactAlarms() if (!canScheduleExactAlarms) { // 未处理权限拒绝情况 } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent ) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent ) } else { alarmManager.set( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent ) } Result.success() } else { Result.failure() } } else { AppUtils.notify(applicationContext,"Error","Please select allow all the time from settings.") Result.failure() } } catch (e: Exception) { Result.failure() } } }
Manifest权限配置:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <!-- or --> <uses-permission android:name="android.permission.USE_EXACT_ALARM" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
现在遇到的问题:设置的2小时后触发的PendingIntent完全没生效,针对Android 14版本找不到解决方案。我知道后台位置请求有次数限制,但我是每2小时触发一次,请问可能的错误原因是什么?
针对你在Android 14上面临的2小时后PendingIntent不触发的问题,以下是几个核心原因及对应的解决思路:
1. 精确闹钟权限未主动申请
Android 12及以上版本,SCHEDULE_EXACT_ALARM权限默认拒绝,即使Manifest声明了权限,也必须主动引导用户到系统设置开启。你当前代码仅判断权限状态但未处理拒绝场景,导致闹钟无法被调度。
解决方法:
当检测到权限未授权时,跳转至系统设置页面:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val canScheduleExactAlarms = alarmManager.canScheduleExactAlarms() if (!canScheduleExactAlarms) { val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) applicationContext.startActivity(intent) AppUtils.notify(applicationContext,"权限提示","需要开启精确闹钟权限以继续获取天气") } }
2. PendingIntent的requestCode重复
你使用固定的requestCode = 1创建PendingIntent,每次调度新闹钟时会覆盖旧的PendingIntent。若旧闹钟未触发就被覆盖,会导致调度失效。
解决方法:
使用唯一的requestCode,比如基于时间戳:
val requestCode = System.currentTimeMillis().toInt() val pendingIntent = PendingIntent.getBroadcast( applicationContext, requestCode, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT )
3. Android 14后台任务限制触发
Android 14对后台空闲应用的任务调度限制更严格,若应用长时间未被用户打开,系统可能推迟或取消闹钟调度。手动维护AlarmManager循环的方式可靠性远低于WorkManager的周期性任务。
解决方法:
改用WorkManager的PeriodicWorkRequest替代手动循环:
// 在BroadcastReceiver中直接调度周期性任务 val periodicRequest = PeriodicWorkRequestBuilder<WeatherWorker>(2, TimeUnit.HOURS) .setConstraints(constraints) .build() WorkManager.getInstance(context!!).enqueueUniquePeriodicWork( "WeatherUpdate", ExistingPeriodicWorkPolicy.REPLACE, periodicRequest )
WorkManager会自动适配系统的后台限制,无需手动维护AlarmManager。
4. 位置获取失败导致Worker提前终止
若fusedLocationClient.lastLocation返回null,Tasks.await(locationResult)会抛出异常,导致Worker直接返回Result.failure(),后续的闹钟调度代码根本不会执行。
解决方法:
增加位置获取的容错逻辑,当lastLocation为空时请求单次位置更新,并改为挂起方式处理:
suspend fun getCurrentLocation(): Location? { return try { fusedLocationClient.lastLocation.await() ?: run { val locationDeferred = CompletableDeferred<Location?>() val callback = object : LocationCallback() { override fun onLocationResult(result: LocationResult) { super.onLocationResult(result) fusedLocationClient.removeLocationUpdates(this) locationDeferred.complete(result.lastLocation) } } fusedLocationClient.requestLocationUpdates( LocationRequest.create().apply { priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY numUpdates = 1 }, callback, Looper.getMainLooper() ) locationDeferred.await() } } catch (e: Exception) { null } } // 在doWork中调用 val location = getCurrentLocation() ?: run { AppUtils.notify(applicationContext,"错误","无法获取当前位置") return Result.retry() // 重试而非直接失败 } val lat = location.latitude val long = location.longitude
5. 位置权限未正确授权
Android 14中ACCESS_BACKGROUND_LOCATION需要用户主动选择"始终允许",若仅授予"仅在使用应用时允许",位置获取逻辑会失败,导致Worker返回失败,后续闹钟调度无法执行。
解决方法:
在Fragment中请求位置权限时,明确包含后台权限:
private val REQUEST_LOCATION_PERMISSION = 1001 // 发起权限请求 fun requestLocationPermissions() { val permissions = arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION ) requestPermissions(permissions, REQUEST_LOCATION_PERMISSION) }
内容的提问来源于stack exchange,提问作者Roni

