如何用AlarmManager定时启停FusedLocationProviderClient的位置更新
解决方案:用AlarmManager定时启停后台位置更新
你的思路完全正确——AlarmManager就是解决这种定时任务的最佳方案,既能精准控制启停时间,又能避免夜间不必要的电量消耗。下面给你一套完整的实现步骤和代码示例:
一、封装位置更新的启停方法
首先把开启和关闭位置更新的逻辑封装成可复用方法,方便后续调用:
// 可放在MainActivity或单独的LocationHelper工具类中 private fun startLocationUpdates() { val locationRequest = LocationRequest.create().apply { interval = 10000 // 根据业务需求调整更新间隔 fastestInterval = 5000 priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY } val pendingIntent = PendingIntent.getBroadcast( this, 0, Intent(this, YourLocationReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) fusedLocationProviderClient.requestLocationUpdates( locationRequest, pendingIntent ) } private fun stopLocationUpdates() { val pendingIntent = PendingIntent.getBroadcast( this, 0, Intent(this, YourLocationReceiver::class.java), PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE ) pendingIntent?.let { fusedLocationProviderClient.removeLocationUpdates(it) it.cancel() } }
二、创建定时任务的广播接收器
新建一个LocationSchedulerReceiver,专门处理AlarmManager触发的启停指令:
class LocationSchedulerReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { context ?: return val fusedClient = LocationServices.getFusedLocationProviderClient(context) when (intent?.action) { ACTION_STOP_LOCATION -> { val pendingIntent = PendingIntent.getBroadcast( context, 0, Intent(context, YourLocationReceiver::class.java), PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE ) pendingIntent?.let { fusedClient.removeLocationUpdates(it) it.cancel() } } ACTION_START_LOCATION -> { val locationRequest = LocationRequest.create().apply { interval = 10000 fastestInterval = 5000 priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY } val pendingIntent = PendingIntent.getBroadcast( context, 0, Intent(context, YourLocationReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) fusedClient.requestLocationUpdates(locationRequest, pendingIntent) } } } companion object { const val ACTION_STOP_LOCATION = "com.yourpackage.action.STOP_LOCATION" const val ACTION_START_LOCATION = "com.yourpackage.action.START_LOCATION" } }
别忘了在AndroidManifest.xml中注册这个接收器:
<receiver android:name=".LocationSchedulerReceiver" android:exported="false"> <intent-filter> <action android:name="com.yourpackage.action.STOP_LOCATION" /> <action android:name="com.yourpackage.action.START_LOCATION" /> </intent-filter> </receiver>
三、设置每日重复的Alarm任务
创建方法配置AlarmManager,分别设置22:00的停止任务和6:00的启动任务:
private fun setupDailyLocationAlarms() { val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager // 配置22:00停止更新的PendingIntent val stopIntent = Intent(this, LocationSchedulerReceiver::class.java).apply { action = LocationSchedulerReceiver.ACTION_STOP_LOCATION } val stopPendingIntent = PendingIntent.getBroadcast( this, 1, // 用不同requestCode区分两个任务 stopIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // 配置6:00开启更新的PendingIntent val startIntent = Intent(this, LocationSchedulerReceiver::class.java).apply { action = LocationSchedulerReceiver.ACTION_START_LOCATION } val startPendingIntent = PendingIntent.getBroadcast( this, 2, startIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // 设置停止任务:每日22:00触发 val stopCalendar = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, 22) set(Calendar.MINUTE, 0) set(Calendar.SECOND, 0) // 如果当前时间已过22:00,自动顺延至次日 if (timeInMillis < System.currentTimeMillis()) { add(Calendar.DAY_OF_YEAR, 1) } } // 设置启动任务:每日6:00触发 val startCalendar = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, 6) set(Calendar.MINUTE, 0) set(Calendar.SECOND, 0) if (timeInMillis < System.currentTimeMillis()) { add(Calendar.DAY_OF_YEAR, 1) } } // 适配Android版本:Android 12+需要SCHEDULE_EXACT_ALARM权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (alarmManager.canScheduleExactAlarms()) { alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, stopCalendar.timeInMillis, AlarmManager.INTERVAL_DAY, stopPendingIntent ) alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, startCalendar.timeInMillis, AlarmManager.INTERVAL_DAY, startPendingIntent ) } else { // 引导用户开启精确闹钟权限 startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)) } } else { alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, stopCalendar.timeInMillis, AlarmManager.INTERVAL_DAY, stopPendingIntent ) alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, startCalendar.timeInMillis, AlarmManager.INTERVAL_DAY, startPendingIntent ) } }
四、首次启动与权限处理
在MainActivity的onCreate中完成首次位置更新开启和闹钟设置,避免重复配置:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this) // 用SharedPreferences标记是否已设置过闹钟,防止重复创建 val sharedPref = getSharedPreferences("LocationPrefs", MODE_PRIVATE) val alarmsSet = sharedPref.getBoolean("alarms_set", false) if (!alarmsSet) { // 首次启动立即开启位置更新 startLocationUpdates() // 设置每日定时任务 setupDailyLocationAlarms() // 标记已完成配置 sharedPref.edit().putBoolean("alarms_set", true).apply() } }
五、处理设备重启(可选但重要)
设备重启后AlarmManager任务会被清除,需添加开机广播接收器重新配置:
- 新建
BootCompletedReceiver:
class BootCompletedReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == Intent.ACTION_BOOT_COMPLETED) { context?.let { ctx -> // 重新设置每日闹钟 val alarmManager = ctx.getSystemService(Context.ALARM_SERVICE) as AlarmManager // 检查当前时段,若在6:00-22:00之间则立即开启位置更新 val currentHour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) if (currentHour in 6..21) { val fusedClient = LocationServices.getFusedLocationProviderClient(ctx) val locationRequest = LocationRequest.create().apply { interval = 10000 fastestInterval = 5000 priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY } val pendingIntent = PendingIntent.getBroadcast( ctx, 0, Intent(ctx, YourLocationReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) fusedClient.requestLocationUpdates(locationRequest, pendingIntent) } // 复用之前的闹钟配置方法(建议将setupDailyLocationAlarms改为静态工具方法) setupDailyLocationAlarms() } } } }
- 在Manifest中注册并添加权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <!-- Android 12+ --> <receiver android:name=".BootCompletedReceiver" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
关键注意事项
- 权限检查:确保已申请
ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION以及ACCESS_BACKGROUND_LOCATION权限,否则后台位置更新无法正常工作。 - Android版本适配:Android 12及以上必须申请
SCHEDULE_EXACT_ALARM权限,且需先检查alarmManager.canScheduleExactAlarms()返回值,若为false则引导用户到设置页开启。 - PendingIntent标记:务必使用
FLAG_IMMUTABLE(Android 12+强制要求),避免安全风险。
内容的提问来源于stack exchange,提问作者Muhammed Kadir
相关产品推荐
相关产品推荐

